问题
使用如下java反射代码:
Method method = LogFactory.class.getDeclaredMethod("getContextClassLoader");
method.setAccessible(true);
ClassLoader classLoader = (ClassLoader)method.invoke(null);
LogFactory.release(classLoader);
使用jprofiler
看到很多类似sun.reflect.GeneratedMethodAccessor11
这种类,在每次调用的时候都增长:
sun.reflect.BootstrapConstructorAccessorImpl
sun.reflect.NativeConstructorAccessorImpl
sun.reflect.DelegatingConstructorAccessorImpl
sun.reflect.DelegatingClassLoader
我觉得这个是PermGen space 增长的原因,但是如何清理掉这些类呢?
网友[路人甲]的一段解释
当使用java反射,有两种方法获取被反射的类的信息。可以使用JNI的方式,也可以使用字节码的方式。如果使用字节码的方式,需要java的类和类加载器(sun/reflect/GeneratedMethodAccessor class and sun/reflect/DelegatingClassLoader)。这些类和类加载器使用本地内存。使用字节码的方式也使用JIT编译,但是这会更加重本地内存的使用。如果频繁使用java反射,这更能带来一个显著的内存使用上的上升。JVM会优先使用JNI的方式,在经历过一些相同的类之后,才会使用字节码的方式。这被称为膨胀效应-当JVM从JNI方式变为字节码的方式。幸运的是,我们可以通过一个Java属性配置,
sun.reflect.inflationThreshold
属性告诉JVM使用JNI方式多少次,如果设置为0,JNI方式将会被一直使用。既然字节码的方式比JNI的方式使用更多内存,如果我们使用Java反射,我们系王世勇JNI的方式。为了实现这个,我们只需要设置inflationThreshold
属性为0即可。
网友[路人乙]的补充
如果使用oracle 的JVM,这样子设置:
sun.reflect.inflationThreshold=2147483647
如果使用IBM JVM,这样设置:
-Dsun.reflect.inflationThreshold=0