从tomcat的永久区溢出看类加载方式以及内存分析工具

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从tomcat的永久区溢出看类加载方式以及内存分析工具相关的知识,希望对你有一定的参考价值。

tomcat热部署导致的溢出:

tomcat设置为热部署状态 reload=‘true’可能会产生永久区的内存溢出,首先永久区存储的是类的class信息,日志报出的信息有CGLIB的报错信息,查了一下有说CGLIB的动态代理占用了大量的永久区,所以再加上tomcat reload时候旧的类信息没有办法gc就导致了永久区内存溢出。

加载方式和热部署原理

java加载方式:

java加载的方式是通过classloader对象加载,自带的classloader有sun.misc.Launcher$AppClassLoader,Bootstrap,bootstrap是由c/c++写的,在虚拟机运行时加载类,AppclassLoader是常用的classloader,通过ClassLoader.getSystemClassLoader方法获得的就是appclassloader对象,除此外还有URLClassLoader,钦定的这些classloader有一个特点叫双亲加载机制:
  1. protected Class<?> loadClass(String name, boolean resolve)
  2. throws ClassNotFoundException
  3. {
  4. synchronized (getClassLoadingLock(name)) {
  5. // First, check if the class has already been loaded
  6. Class<?> c = findLoadedClass(name);
  7. if (c == null) {
  8. long t0 = System.nanoTime();
  9. try {
  10. if (parent != null) {
  11. c = parent.loadClass(name, false);
  12. } else {
  13. c = findBootstrapClassOrNull(name);
  14. }
  15. } catch (ClassNotFoundException e) {
  16. // ClassNotFoundException thrown if class not found
  17. // from the non-null parent class loader
  18. }
  19. if (c == null) {
  20. // If still not found, then invoke findClass in order
  21. // to find the class.
  22. long t1 = System.nanoTime();
  23. c = findClass(name);
  24. // this is the defining class loader; record the stats
  25. sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
  26. sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
  27. sun.misc.PerfCounter.getFindClasses().increment();
  28. }
  29. }
  30. if (resolve) {
  31. resolveClass(c);
  32. }
  33. return c;
  34. }
  35. }
这个classloader的方法可以看出双亲委托机制:
  1. 1. 当前ClassLoader首先从自己已经加载的类中查询是否此类已经加载,如果已经加载则直接返回原来已经加载的类。每个类加载器都有自己的加载缓存,当一个类被加载了以后就会放入缓存,等下次加载的时候就可以直接返回了。
  2. 2. 当前classLoader的缓存中没有找到被加载的类的时候,委托父类加载器去加载,父类加载器采用同样的策略,首先查看自己的缓存,然后委托父类的父类去加载,一直到bootstrp ClassLoader.
  3. 3. 当所有的父类加载器都没有加载的时候,再由当前的类加载器加载,并将其放入它自己的缓存中,以便下次有加载请求的时候直接返回。
所以如果想实现热加载,就要摆脱双亲委托的机制,可以看到load方法并不是final所以是可以自定义的,还有一点需要注意的是类的命名空间是由全类名加类加载器所构成的,也就是说类加载器不同所加载出来的类就会钦定为不同,所以可能出现
  1. com.wks.TestReloadClass.Hot cannot be cast to com.wks.TestReloadClass.Hot
的错误,还有要实现热加载,classloader每次都要new一个新的,不然一次之后就不能再加载同一个类了,
对于已经加载了得类信息,由于jvmgc的不确定性,很不容易被gc掉,必须要所有引用都null,包括所对应的classloader,这也是tomcat永久区溢出的原因

热加载的实现:

  1. -verbose:class //vm参数,看class的加载和卸载信息
代码见附件

内存分析工具jvisualvm

jdk自带的内存分析工具,在jre/bin目录下,可以监控正在运行的java程序,查看cpu 内存,类,对象,也可以查看dmp文件,通过监控tomcat的
reload,看到了多个同一类的类信息:

参考

http://www.blogjava.net/heavensay/archive/2012/11/07/389685.html




附件列表

     








    以上是关于从tomcat的永久区溢出看类加载方式以及内存分析工具的主要内容,如果未能解决你的问题,请参考以下文章

    JVM各种情况内存溢出分析

    tomcat设定shared lib共享同样的jar

    tomcat 内存溢出问题(OutOfMemoryError: PermGen space)

    JVM记一次PermGen space内存溢出实战案例

    JVM内存分区和各分区溢出测试

    JVM内存分区和各分区溢出测试