于吉吉的技术博客

建造高性能门户网

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  65 随笔 :: 6 文章 :: 149 评论 :: 0 Trackbacks

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

一般来说PermSize Space OOM的话,第一种可能就是方法区溢出,第二种就是运行时常量池溢出,第二种查看后基本排除掉,问题就应该出现在方法区的溢出,方法区用于存放class的相关信息,如类名,访问修饰符,常量池,字段描述,方法描述等等,对于这个区域的溢出,基本上都是运行时产生大量的类填满了整个方法区,直到溢出。

spring aop中都是使用到了cglib这类字节码的技术,动态代理的类越多,就需要越多的方法区来保证动态生成的class可以加载入到内存中去,
不过spring框架导致的不会因为这种原因。撑爆perm的应该是各种methodaccessorX和constructoracccessorX等等。本来这些accessor也有缓存,但它们使用内存大小敏感的reference引用着的,且使用的是堆内存。当你堆内存吃紧的时候,这些缓存就摧毁了,就必然会不断产生新的methodAccessor字节码,是这个撑爆了perm。所以除增大permsize还应该看看平时运行时堆内存是不是经常用光

下面的例使用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和加载类的情况并没有呈现直线上涨,已经他们一直都使用者动态类生成类的静态缓存,但是这种动态创建类使用静态缓存在一些情况下并不适合需求。


posted on 2011-08-21 15:55 陈于喆 阅读(6393) 评论(0)  编辑  收藏 所属分类: web开发java

只有注册用户登录后才能发表评论。


网站导航: