一篇大神的译文,勉强(嗯。。相当勉强)地放在类加载器系列吧,第8弹:
一、前言手里是锤子,看哪里都是钉子。最近学习类加载器的感觉就是如此,总是在想,利用它可以做到什么? 可以做到类隔离、不停服务执行动态调试代码,但是,还能做什么呢?
毕竟,Tomcat 出到现在了,也不支持更新某一个class 而不重启应用(这里重启应用的意思是,不是重启 Tomcat,而是重新部署 webapp),而热部署同样也是一个耗时的操作。有经验的同学应该知道Jrebel,开发环境的神器,有了它,平时用开发机和前端同学联调,再也不用频繁重启应用了。Jrebel可以做到动态更新某个class,并且可以马上生效,但是它的实现原理是迂回了一圈去解决这个问题的,且会有性能上的损耗,所以在生产环境也是不建议的(jrebel原理参考:HotSwap和JRebel原理)。
按理说,Java 出现都20几年了,这样的需求还没解决,背后是有什么样的原因吗?这里,我找到一篇 jRebel 网站上的文章,感觉写得很好,这里勉强利用我的渣英语翻译一下。如果英语底子好,直接看原文吧。
链接:Reloading Java Classes 101: Objects, Classes and ClassLoaders
ps:翻译到最后,发现这篇文章就是 JRebel的作者写的,大家看看下面的截图:
再看看维基百科:
https://en.wikipedia.org/wiki/ZeroTurnaround
二、正文
在这篇文章里,我们将讨论怎么利用动态的类加载器去热更一个 Java 类。同时,我们会先看看,对象、类、类加载器是怎么互相紧密绑在一起的,然后再看看为了达到热更的目的,需要做出的努力。我们将从一个问题开始,见微知著,解释热更的过程,然后通过一个特定的例子来展示这其中会遇到的问题和解决方案。本系列文章包括:
RJC101: Objects, Classes and ClassLoaders
RJC201: How do Classloader leaks happen?
RJC301: Classloaders in Web Development — Tomcat, GlassFish, OSGi, Tapestry 5 and so on
RJC401: HotSwap and JRebel — Behind the Scenes
RJC501: How Much Does Turnaround Cost?
管中窥豹
谈论Java class 热更之前的第一件事,就是理解类和对象的关系。任何 java 代码,都和包含在类中的方法紧密关联。简单来说,你可以把一个类,想成一个方法的集合,这些方法接收 “this” 关键字作为第一个参数。(译者:可以把深入理解JVM那本书拿出来翻一下了,见下图)。
类被装载进内存,并被赋予一个唯一标识。在 Java api中,你可以通过 MyObject.class 这样的方式来获得一个 java.lang.Class 的对象,这个对象就能唯一标识被加载的这个类。
每个被创建的对象,都能通过 Object.class 来获得对这个类的唯一标识的引用。当在该对象上调用一个方法时,JVM 会在内部获取到 class 引用,并调用该 class 的方法。也就是说,假设 mo 是 MyObject 类的一个对象,当你调用 mo.method()时, JVM 实际会进行类似下面这样的调用: mo.getClass().getDeclaredMethod("method").invoke(mo) (虚拟机实现并不会这样写,但是最终的结果是一致的)
因此,每一个对象都和它的类加载器相关联(MyObject.class.getClassloader())。 classLoader 的主要作用就是去定义类的可见范围——在什么地方这个类是可见的,什么地方又是不可见的。 这样的范围控制,允许具有相同包名及类名的类共存,只要他们是由不同的类加载加载的。该机制也允许在一个不同的类加载器中,加载一个新版本的类。