Skip to content

Jrebel原理剖析

by argan on 三月 7th, 2010

zeroturnaround的blog上发表了一篇文章,描述了一些 jrebel实现的细节,从中可以分析出一些技巧来。

JRebel makes use of two remarkable features of the JVM — abstract bytecode and classloaders. Classloaders allow JRebel to recognize the moment when a class is loaded, then translate the bytecode on-the-fly to create another layer of abstraction between the virtual machine and the executed code.

从描述看得出来,jrebel是通过在class loading的时候,通过字节码处理,生成一套自有的类结构来,和hot swap不同,他工作在ClassLoader这一层,而hotswap是工作在JVM这一层的。总的来说,jrebel采用了类似动态语言(jruby)的机制来实现class reloading(新产生类,而不是修改类),因为纠结在hot swap上是没希望的,固有的问题无法解决(class一旦装载就无法修改)。

动态语言一般的机制是将“类”实现为一个holder,只是一个入口,真正的里面的方法都是在关联的一些匿名的类里,所谓的动态,其实就是在新增/修改方法的时候,产生一个新的类,并且关联到那个holder上去,从调用的层面上来看,就实现了“动态”改变一个类的行为。

但是,这种方案也有一些缺陷

  • Perfomance. Such a setup would mean that each method invocation would be subject to indirection. We could optimize, but the application would be at least an order of magnitude slower. Memory use would also skyrocket, as so many classes are created.
  • Java SDK classes. The classes in the Java SDK are considerably harder to process than the ones in the application or libraries. Also they often are implemented in native code and cannot be transformed in the “JRuby” way. However if we leave them as is, then we’ll cause numerous incompatibility errors, which are likely not possible to work around.
  • Compatibility. Although Java is a static language it includes some dynamic features like reflection and dynamic proxies. If we apply the “JRuby” transformation none of those features will work unless we replace the Reflection API with our own classes, aware of the transformation.

可以看到,主要的问题,一个是性能,一方面内存消耗变多,另一方面,每一次方法调用都是间接的(找到一个类,再去执行),而不是直接的在原来的类上进行调用

还有,就是JDK里面包含的类,很难处理,无法用这种机制来转换,导致很多不兼容的错误,很多场景无法工作

还有一个兼容性的问题,如果使用java的反射机制,这种机制和原来的类结构是完全不一样的,现在越来越多的类库或者框架都很依赖反射的,这是一个大的问题。

因此,jrebel需要解决上述问题,

  • Leaves as many method invocations intact as possible. This means that JRebel minimizes its performance overhead, making it lightweight.
  • Avoids instrumenting the Java SDK except in a few places that are necessary to preserve compatibility.
  • Tweaks the results of the Reflection API, so that we can correctly include the added/removed members in these results. This also means that the changes to Annotations are visible to the application.

为了性能影响最小,jrebel将尽量多的方法调用都是直接的方法执行,而不是绕一圈再去,这样可以变得轻量一点,不管是内存消耗还是性能损耗上

尽量避免去搞JDK里的类,除了个别必须的以外,例如ClassLoader和reflection api相关的类(例如,Class,Field,Method,Constructor等),处理好了反射相关的类,就可以兼容原有的反射功能的表现了,达到兼容的目的。

从上面的分析,我们可以来猜测jrebel的实现原理:

首先,需要通过instrument来修改ClassLoader和Reflection相关的类,加入自己的入口(比如监测类文件或者资源改变)

然后,在装载一个需要实现reload的类的时候,在我们修改过的ClassLoader里面,修改类的结构,加入特有的一些信息,比如一些call back啊啥的,因为我们无法修改已经装载的类,只能通过产生新的类来实现,因此在装载类A的时候,实际上我们是产生了一个A1给应用使用,通过一些字节码处理来达到和类A兼容,当类A的字节码修改的时候,我们再生成一个A2给应用,同时我们可以通过A的一个holder来保存一些运行状态,实现真正的“无缝”reload。

另外,修改的Reflection相关API,用来实现引用里使用reflection的时候的表现和原来一致,比如,我们应用得到和使用的是A1或者A2,如果我在这上面做reflection,那岂不是得到一个A1/A2么,那肯定不行,因此要hack reflection API,告诉应用我们还是叫A,而不是A1/A2,对field和method的处理也一样。

欢迎讨论,原文在 http://www.zeroturnaround.com/blog/reloading_java_classes_401_hotswap_jrebel/

VN:D [1.9.3_1094]
Rating: 8.0/10 (1 vote cast)
VN:D [1.9.3_1094]
Rating: +1 (from 1 vote)
Jrebel原理剖析, 8.0 out of 10 based on 1 rating
» 转载请注明来源:贰号楼肆层 » 《Jrebel原理剖析》
No comments yet

Leave a Reply

Note: XHTML is allowed. Your email address will never be published.

Subscribe to this comment feed via RSS