首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

动态加载跟卸载Java类

2012-10-25 
动态加载和卸载Java类在开发Java服务器应用时,我们最希望开发的应用能够支持热部署,即不需要重启系统就可

动态加载和卸载Java类
在开发Java服务器应用时,我们最希望开发的应用能够支持热部署,即不需要重启系统就可以用新的应用替换旧的应用。
如果使用动态语言,这些功能比较容易实现。但Java是静态语言,是否也可实现动态热部署呢?

首先,我们要深入了解一下Java的类装载(Class Loading)机制,和垃圾回收(Garbage Collection)机制。其中class loading 将负责装载新的应用包;GC将负责卸载旧的应用包。
装载新应用包的方法比较简单,只需要定制一个ClassLoader,从指定路径装载.jar文件即可。
要卸载一个ClassLoader,则必须要同时卸载通过该ClassLoader 载入的类的所有实例, 简单将ClassLoader的引用置为null并希望GC回收的做法是无效的。然而,要想统计并记录所有该ClassLoader载入的类实例是不现实的。而且,应用的装载和卸载功能,是服务器Framework的一部分,而不是应用业务功能的一部分。因此,framework也无法知道业务应用中何时载入和创建了多少对象。

解决方法还是有的。因为GC回收Heap中那些unreachable的对象,追根溯源,所有对象的创建都是由活动线程中发起的, 即所谓的Root set of references. 因此一旦活动线程结束运行,则可以说,线程中所有对象的根引用不可用了,则由根应用创建的所有对象也变为unreachable.

因此可以做如下设计:


其中Server负责service的deploy和undeploy。
其中deploy的过程可简单描述为:创建一个daemon线程,设置其context classloader为Service Jar包的ClassLoader,起动该Daemon线程。
undeploy的过程可简单描述为:停止服务(service daemon thread),设置线程context classloader为null, 等带线程彻底结束后手动执行GC来回收对象和ClassLoader.

Service接口如下:




示例服务代码,测试修改编译后重新部署:


控制台输出如下:
Server started.
org.lucky.server.MyJarLoader@27b15692 is created.
== org.lucky.service.example.ExampleService1@4e76fba0 is installed.
== org.lucky.service.example.ExampleService1@4e76fba0 is started.
org.lucky.service.example.ExampleService1@4e76fba0  10000
org.lucky.service.example.ExampleService1@4e76fba0  9999
org.lucky.service.example.ExampleService1@4e76fba0  9998
org.lucky.service.example.ExampleService1@4e76fba0  9997
org.lucky.server.MyJarLoader@2f833eca is created.
== Trying to stop org.lucky.service.example.ExampleService1@4e76fba0
== org.lucky.service.example.ExampleService1@4e76fba0 is uninstalled.
== org.lucky.service.example.ExampleService1@4e76fba0 is stopped.
org.lucky.service.example.ExampleService1@4e76fba0 - is finalized.
org.lucky.server.MyJarLoader@27b15692 is finalized.
== org.lucky.service.example.ExampleService1@7b36a43c is installed.
== org.lucky.service.example.ExampleService1@7b36a43c is started.
org.lucky.service.example.ExampleService1@7b36a43c  0
org.lucky.service.example.ExampleService1@7b36a43c  1
org.lucky.service.example.ExampleService1@7b36a43c  2
org.lucky.service.example.ExampleService1@7b36a43c  3

由上述测试可见,通过这种方式,可以实现类动态加载和卸载以及热部署。

这个方法为Java类的动态加载/卸载提供了一个思路。由于它需要为每一个需要部署的服务起动一个线程,虽然该线程只负责装载和卸载服务,服务运行时并不消耗CPU,但会固定占用一定大小的内存。测试值为每线程约占用350k内存。因此服务多时,存在StackOverflowError的风险。

在JDK7中据说提供了anonymous classloading 机制来支持动态类加载/卸载。需要使用的同学可以关注一下。 1 楼 deepnighttwo 2011-07-11   想卸载service1的上海,如果别的service持有哪怕一个service1中创建的对象,那也是无法卸载成功的。

所以这个方法限制非常大,如果想卸载一个service,要求service之前只能通过中间数据格式(比如xml,json)来交换数据。或者通过每个service公共的parent class loader加载的类的实例来交换数据。

如果作为一个框架放开了用的话,一旦别的service持有了另一个service创建出来的对象,那就是灾难。

热点排行