`
touchmm
  • 浏览: 1005289 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

简单分析cglib引起的PermSize Space内存溢出

 
阅读更多
上段时间有台机器发生了 java.lang.OutOfMemoryError: PermGen space 内存溢出的异常,当时大概判断了原因后就把 MaxPermSize 配置调高后,就把问题解决了,不过空下时间后还是需要继续把review代码。

一般来说PermSize Space OOM的话,第一种可能就是方法区溢出,第二种就是运行时常量池溢出,第二种查看后基本排除掉,问题就应该出现在方法区的溢出,方法区用于存放class的相关信息,如类名,访问修饰符,常量池,字段描述,方法描述等等,对于这个区域的溢出,基本上都是运行时产生大量的类填满了整个方法区,直到溢出。
spring aop中都是使用到了cglib这类字节码的技术,动态代理的类越多,就需要越多的方法区来保证动态生成的class可以加载入到内存中去,下面的例使用cglib直接进行动态代理产生大量的动态类,然后使用jconsole进行观察。

首先将本机的jvm配置为 -XX:PermSize=64M -XX:MaxPermSize=64M ,给到PermSize最大为64M的内存

 

public   class  PermgenOOM {

    
public   static   void  main(String[] args)  throws  InterruptedException {
        
int  i = 0 ;
        
while ( true ){
            Enhancer enhancer 
=   new  Enhancer();
            enhancer.setSuperclass(Product.
class );
            enhancer.setUseCache(
false ); //  关闭CGLib缓存,否则总是生成同一个类
            enhancer.setCallback( new  MethodInterceptor() {                
                @Override
                
public  Object intercept(Object obj, Method method, Object[] args,
                        MethodProxy methodproxy) 
throws  Throwable {
                    
//  TODO Auto-generated method stub
                     return  methodproxy.invokeSuper(obj,args);
                }
            });
            enhancer.create();
            Thread.sleep(
100 );
        }
    }
}

 

很快,系统就抛出了 java.lang.OutOfMemoryError: PermGen space
内存池peimgen的情况


加载类的情况



并且在方法区中,一个类如果要被垃圾收集器回收掉,判断的条件是非常苛刻的,很多人都把方法区称为“永久区”(Permanent Generation),但据说2者在本质上是不一致的,另外还有称呼为“非堆区”,不纠结这个了。

再看看enhancer.setUseCache(false),如果选择为true的话,那么就使用和更新一类具有相同属性生成的类的静态缓存,而不会在同一个类文件还继续被动态加载并视为不同的类,这个其实跟类的equals()和hashCode()有关,它们是与cglib内部的class cache的key相关的。

将上面的程序 enhancer.setUseCache(false) 改为 enhancer.setUseCache(ture)

 

public   class  PermgenOOM {

    
public   static   void  main(String[] args)  throws  InterruptedException {
        
int  i = 0 ;
        
while ( true ){
            Enhancer enhancer 
=   new  Enhancer();
            enhancer.setSuperclass(Product.
class );
            enhancer.setUseCache(
true ); //  或者不写,默认值就是true
            enhancer.setCallback( new  MethodInterceptor() {                
                @Override
                
public  Object intercept(Object obj, Method method, Object[] args,
                        MethodProxy methodproxy) 
throws  Throwable {
                    
//  TODO Auto-generated method stub
                     return  methodproxy.invokeSuper(obj,args);
                }
            });
            enhancer.create();
            Thread.sleep(
100 );
        }
    }
}

 

内存池peimgen的情况


加载类的情况



可以发现内存池peimgen和加载类的情况并没有呈现直线上涨,已经他们一直都使用者动态类生成类的静态缓存,但是这种动态创建类使用静态缓存在一些情况下并不适合需求。
2
0
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics