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

JVM中Perm区持续下涨有关问题

2013-01-28 
JVM中Perm区持续上涨问题公司一位大牛在微博上的一条,打算消化一下,毕竟今后Perm区的上涨还是有可能遇到的

JVM中Perm区持续上涨问题

公司一位大牛在微博上的一条,打算消化一下,毕竟今后Perm区的上涨还是有可能遇到的。“Java应用Perm区一直呈上涨趋势的原因可以用一个简单的办法排查,就是用btrace去跟踪下是什么地方在调用ClassLoader.defineClass,在大多数情况下这招都是管用的。”

(1)Perm区存放的啥信息?

????????Perm叫做持久代,存放了类的信息、类的静态变量、类中final类型的变量、类的方法信息,Perm是全局共享的,在一定条件下会被GC掉,方所要承载的数据超过内存区域后,就会出现OOM异常。可以通过-XX:PermSize和-XX:MaxPermSize来指定这个区域的最小值和最大值。持久代的回收主要包括两部分,废弃常量和无用的类,废弃的常量比较容易判别,没有任何String类型的对象引用这个就算可以废弃的了,但是无用的类判别比较复杂,(该类所有的实例已经被回收,JVM中没有任何类的实例;加载该类的ClassLoader被回收;该类对应的java.lang.class没有地方引用)。可以使用-verbose:class以及-XX:TraceClassLoading和-XX:TraceClassUnLoading来查看类的加载和卸载情况。

(2)ClassLoader中的defineClass是干啥的?

????????该方法用于将二进制的字节码转换为Class对象,对于自定义加载类非常重要,如果二进制的字节码不符合JVM的规范,就会报ClassFormatError,如果生成的类名和二进制中的不符,报NoClassDefFoundError异常,如果加载的class是受保护的,则报SecurityException,如果此类已经在ClassLoader已经加载,会报LinkageError。

?

例子1:运行时常量溢出

向常量池中添加数据,可以调用String.intern(),这个是native方法,如果常量池中已经存在一个,则返回,否则添加早常量池中。

刚开始的例子如下:

123456789public?class?PermTest {????????public?static?void?main(String[] args) {????????????int?i =?0;????????????while(true){????????????????String.valueOf(i++).intern();????????????????System.out.println(i);????????????}????????}}

发现没有抛出OOM异常,用JConsole查看,(PermSize和MaxPermSize都是10M)在10M的时候曲线又下去了,原来是FullGC把常量池中没有的引用GC掉了,所以没有OOM。

增加了一个List,持有这个引用,就不会被GC掉了。

123456789public?class?PermTest {????????public?static?void?main(String[] args) {????????????List<String> all =?new?ArrayList<String>();????????????int?i =?0;????????????while(true){????????????????all.add(String.valueOf(i++).intern());????????????}????????}}123Exception in thread?"main"?java.lang.OutOfMemoryError: PermGen space????at java.lang.String.intern(Native Method)????at PermTest.main(PermTest.java:10)

?

例子2:用cglib产生代理类,不断的创建类

1234567891011121314151617181920212223import?java.lang.reflect.Method;import?net.sf.cglib.proxy.Enhancer;import?net.sf.cglib.proxy.MethodInterceptor;import?net.sf.cglib.proxy.MethodProxy;public?class?PermTest {????public?static?void?main(String[] args) {????????while(true){????????????Enhancer enhancer =?new?Enhancer();????????????enhancer.setSuperclass(PermTestClass.class);????????????enhancer.setUseCache(false);????????????enhancer.setCallback(new?MethodInterceptor() {????????????????@Override????????????????public?Object intercept(Object obj, Method method, Object[] args,????????????????????????MethodProxy proxy)?throws?Throwable {????????????????????return?proxy.invoke(obj, args);????????????????}????????????});????????????enhancer.create();????????}????}????static?class?PermTestClass{????}}12345Caused by: java.lang.OutOfMemoryError: PermGen space????at java.lang.ClassLoader.defineClass1(Native Method)????at java.lang.ClassLoader.defineClassCond(ClassLoader.java:631)????at java.lang.ClassLoader.defineClass(ClassLoader.java:615)????...?8?more

Btrace的脚本文件如下(查看哪里调用了defineClass信息):

1234567891011121314151617181920212223242526272829303132import?static?com.sun.btrace.BTraceUtils.*;import?com.sun.btrace.annotations.*;@BTracepublic?class?BtraceAll {????@TLS????private?static?long?beginTime;????@OnMethod(????????????clazz="java.lang.ClassLoader",????????????method="defineClass"????)????public?static?void?traceMethodBegin(){????????beginTime = timeMillis();????}????@OnMethod(????????????clazz="java.lang.ClassLoader",????????????method="defineClass",????????????location=@Location(Kind.RETURN)????)????public?static?void?traceMethdReturn(????????????@Return?String result,????????????@ProbeClassName?String clazzName,????????????@ProbeMethodName?String methodName){????????println("===========================================================================");????????println(strcat(strcat(clazzName,?"."), methodName));????????println(strcat("Time taken : ", str(timeMillis() - beginTime)));????????println("java thread method trace:---------------------------------------------------");????????jstack();????????println("----------------------------------------");????????println(strcat("Reuslt :",str(result)));????????println("============================================================================");????}}

运行后,发现cglib在创建代理类的时候,确实调用了defineClass方法

12345678910111213141516===========================================================================java.lang.ClassLoader.defineClassTime taken :?79java thread method trace:---------------------------------------------------java.lang.ClassLoader.defineClass(ClassLoader.java:615)sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)java.lang.reflect.Method.invoke(Method.java:597)net.sf.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:384)net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:219)net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:377)net.sf.cglib.proxy.Enhancer.create(Enhancer.java:285)PermTest.main(PermTest.java:19)----------------------------------------Reuslt :class?PermTest$PermTestClass$$EnhancerByCGLIB$$1ed16944_8============================================================================

至此,问题场景以及如何排查,清晰了。。。?

参考书籍:

1、分布式java应用基础

2、深入理解JVM

?

?

?

热点排行