一 class的热替换
ClassLoader中重要的方法
...
下面看一个class热加载的例子:
代码:HotSwapURLClassLoader自定义classloader,实现热替换的关键
((HotSwapURLClassLoader)cl).resolveClass(clazz);
cacheLastModifyTimeMap.put(name,lastModifyTime);
}
}
@Override
}
String path = getClassCompletePath(name);
File file = }
}
}
}
String simpleName = name.substring(name.lastIndexOf(".")+1);
}
}
代码:Hot被用来修改的类
System.out.println(" version 1 : "+ }
}
代码:TestHotSwap测试类
}
}
@Override
initLoad();
Object hot = hotClazz.newInstance();
Method m = hotClazz.getMethod("hot");
m.invoke(hot, Thread.sleep(10000);
}
} e.printStackTrace();
}
}
hotSwapCL = HotSwapURLClassLoader.getClassLoader();
hotClazz = hotSwapCL.loadClass(className);
}
}
在测试类运行的时候,修改Hot.class文件
Hot.class
改后第五行:System.out.println(" version 2 : "+this.getClass().getClassLoader());
输出
所以HotSwapURLClassLoader是重加载了Hot类 。注意上面,其实当加载修改后的Hot时,HotSwapURLClassLoader实例跟加载没修改Hot的HotSwapURLClassLoader不是同一个。
图:HotSwapURLClassLoader加载情况
总结:上述类热加载,需要自定义ClassLoader,并且只能重新实例化ClassLoader实例,利用新的ClassLoader实例才能重新加载之前被加载过的class。并且程序需要模块化,才能利用这种热加载方式。
二 class卸载
在Java中class也是可以unload。JVM中class和Meta信息存放在PermGen space区域。如果加载的class文件很多,那么可能导致PermGen space区域空间溢出。引起:java.lang.OutOfMemoryErrorPermGen space. 对于有些Class我们可能只需要使用一次,就不再需要了,也可能我们修改了class文件,我们需要重新加载 newclass,那么oldclass就不再需要了。那么JVM怎么样才能卸载Class呢。
JVM中的Class只有满足以下三个条件,才能被GC回收,也就是该Class被卸载(unload):
- 该类所有的实例都已经被GC。
- 加载该类的ClassLoader实例已经被GC。
- 该类的java.lang.Class对象没有在任何地方被引用。
GC的时机我们是不可控的,那么同样的我们对于Class的卸载也是不可控的。
例子:
代码:SimpleURLClassLoader,一个简单的自定义classloader
}
代码:A
代码:TestClassUnload,测试类
System.out.println("GC over");
}
}
运行的时候配置VM参数: -verbose:class;用于查看class的加载与卸载情况。如果用的是Eclipse,在Run Configurations中配置此参数即可。
图:Run Configurations配置
输出结果
[Loaded java.net.URI$Parser from E:\java\jdk1.7.0_03\jre\lib\rt.jar]
......
上面输出结果中的确A.class被加载了,然后A.class又被卸载了。这个例子中说明了,即便是class加载进了内存,也是可以被释放的。
图:程序运行中,引用没清楚前,内存中情况
图:垃圾回收后,程序没结束前,内存中情况
三 Tomcat中关于类的加载与卸载
Tomcat中与其说有热加载,还不如说是热部署来的准确些。因为对于一个应用,其中class文件被修改过,那么Tomcat会先卸载这个应用(Context),然后重新加载这个应用,其中关键就在于自定义ClassLoader的应用。这里有篇文章很好的介绍了tomcat中对于ClassLoader的应用,请点击here。
Tomcat启动的时候,ClassLoader加载的流程:
1 Tomcat启动的时候,用system classloader即AppClassLoader加载{catalina.home}/bin里面的jar包,也就是tomcat启动相关的jar包。
2 Tomcat启动类Bootstrap中有3个classloader属性,catalinaLoader、commonLoader、sharedLoader在Tomcat7中默认他们初始化都为同一个StandardClassLoader实例。具体的也可以在{catalina.home}/bin/bootstrap.jar包中的catalina.properites中进行配置。
3 StandardClassLoader加载{catalina.home}/lib下面的所有Tomcat用到的jar包。
4 一个Context容器,代表了一个app应用。Context-->WebappLoader-->WebClassLoader。并且Thread.contextClassLoader=WebClassLoader。应用程序中的jsp文件、class类、lib/*.jar包,都是WebClassLoader加载的。
Tomcat加载资源的概况图:
当Jsp文件修改的时候,Tomcat更新步骤:
1 但访问1.jsp的时候,1.jsp的包装类JspServletWrapper会去比较1.jsp文件最新修改时间和上次的修改时间,以此判断1.jsp是否修改过。
2 1.jsp修改过的话,那么jspservletWrapper会清除相关引用,包括1.jsp编译后的servlet实例和加载这个servlet的JasperLoader实例。
3 重新创建一个JasperLoader实例,重新加载修改过后的1.jsp,重新生成一个Servlet实例。
4 返回修改后的1.jsp内容给用户。
图:Jsp清除引用和资源
当app下面的class文件修改的时候,Tomcat更新步骤:
1 Context容器会有专门线程监控app下面的类的修改情况。
2 如果发现有类被修改了。那么调用Context.reload()。清楚一系列相关的引用和资源。
3 然后创新创建一个WebClassLoader实例,重新加载app下面需要的class。
图:Context清除引用和资源
在一个有一定规模的应用中,如果文件修改多次,重启多次的话,java.lang.OutOfMemoryErrorPermGen space这个错误的的出现非常频繁。主要就是因为每次重启重新加载大量的class,超过了PermGen space设置的大小。两种情况可能导致PermGen space溢出。一、GC(Garbage Collection)在主程序运行期对PermGen space没有进行清理(GC的不可控行),二、重启之前WebClassLoader加载的class在别的地方还存在着引用。这里有篇很好的文章介绍了class内存泄露-here
参考:
http://blog.csdn.net/runanli/article/details/2972361(关于Class类加载器 内存泄漏问题的探讨)
http://www.ibm.com/developerworks/cn/java/j-lo-hotswapcls/(Java 类的热替换 —— 概念、设计与实现)
http://www.iteye.com/topic/136427(classloader体系结构)