`

Permanent generation

阅读更多

版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
http://fallenlord.blogbus.com/logs/57543373.html

 

众所周知,Java从1.2开始引入分带GC策略,JVM内存被分成了3个带:young generate、tenured generation和permanent generation
前面两个带相信大家已经非常熟悉了,一般我们所说的GC主要是在这两个带里面运作,我们这里主要讨论Permanent Generation

Perm带是存储类元数据信息的地方,一直以来大家都认为是不会被GC的——确实,类元数据信息被回收了别的类怎么玩?

要说明这个问题,主要需要弄清楚Perm带除了元数据信息外还存了些什么?
栈存基本类型和引用、堆存对象,这个简单的道理大家都懂,但真的是所有的基本类型都存在栈里吗?不见得
还是那句话——无码无真相,我们先从最简单的例子来看一个问题,下面这段代码相信大家都看过很多遍了:

@Test
public void literal() {
    String a = "abc";
    String b = "abc";
    Assert.assertTrue(a == b);
}

没啥好说的,字符串字面量在编译期就会被编译器直接植入.class文件常量池中,并在运行期被JVM当做常量加载,所有存储超过一个字节大小的基本类型都会被编译器优化成这样,这点用javap反编译看下汇编如何压栈的就知道了。关键问题是,常量池在运行期是放在堆里的还是放在栈里的?——答案是都不在

JVM Spec中的Runtime Data Area分为5个区域:pc register、java stack、native stack、java heap、method area,前三个和大多数语言类似比较容易理解,java Heap就是我们常说的堆了,也是Young Generation和Tenured Generation所在,而Method Area就是我们所说的Permanent Generation,上面代码中的字面字符串常量就存在了这里(也有人认为PermGen属于广义上的Heap)

不信?OK,看看下面的代码:

@Test
public void permGenOOM() {
    List<String> list = new ArrayList<String>();
    for (int i = 0; i < Integer.MAX_VALUE; i++) {
        String t = String.valueOf(i).intern();
        list.add(t);
    }
}

运行前先设置下JVM参数:-XX:PermSize=2M -XX:MaxPermSize=4M,将PermSize调小点,这样比较容易出结果
执行一下,PermGen应该很快就爆了

这里用到了String的intern方法,作用我就不多说了,如果不了解的可以自己看看JDK API,大致就是将一个字符串变量转存到常量区的String Pool中,和直接写字面常量是一样的效果,以下代码可以证明:

@Test
public void literalAndIntern() {
    String a = "abc";
    String b = new String("abc");
    Assert.assertFalse(a == b);
    Assert.assertTrue(a == b.intern());
}

OK,既然知道了Permanent Generation中还存着这个东东,那么我们就可以试验GC了
去掉上一段代码的第3和第6行,也就是整个程序不再持有创建出来的intern对象的引用,使得对象可以被GC,同事在JVM参数中追加GC观察-verbose:gc -XX:+PrintGCDetails

@Test
public void permGenGC() {
    for (int i = 0; i < Integer.MAX_VALUE; i++) {
        String t = String.valueOf(i).intern();
    }
}

运行代码,这次是不是没有PermGen OOM了?观察Console中的GC日志,看到很多minor GC,我们主要关注Major GC(Full GC)的内容:
[Full GC [Tenured: 340K->340K(4096K), 0.0197170 secs] 959K->340K(5056K), [Perm : 4096K->799K(4096K)], 0.0199235 secs] [Times: user=0.03 sys=0.00, real=0.03 secs]
看到Perm段,显示PermGen总大小为4096K,此次full gc将其从4096K清理到了799K

到这里应该已经大功告成了,这篇帖子的主题也达到了——GC会清理PermGen

但事情还不算完,既然GC会去动PermGen,那是否会清理类元数据信息呢?虽然看起来很荒谬的理论,但是还是值得尝试一下的:

@Test
public void permGenCglibOOM() {
    for (int i = 0; i < Integer.MAX_VALUE; i++) {
        createInstance();
    }
}

private static ValueObject createInstance() {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(ValueObject.class);
    enhancer.setUseCache(false);  // 关闭CGLib缓存,否则总是生成同一个类
    enhancer.setCallback(new MethodInterceptor() {
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
                throws Throwable {
            return proxy.invokeSuper(obj, args);
        }
    });
    return (ValueObject) enhancer.create();
}

public static class ValueObject {

    private String username = "guolin";

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }
}

这里我们仿造Hibernate用CGLib动态构造了一堆ValueObject的子类,如愿以偿,很快PermGen就OOM了,观察Full GC日志,PermGen一点都没压下去
[Full GC [Tenured: 1950K->1560K(4096K), 0.0323010 secs] 2637K->1560K(5056K), [Perm : 4095K->4095K(4096K)], 0.0323519 secs] [Times: user=0.03 sys=0.00, real=0.03 secs]

打开Yourkit Memory 一栏,看到 loaded classes and unloaded classes. 既然有unloaded classes, which means the classes can be unloaded.

来分析一下原因,我们生成的classes,是否是因为被引用才不会full gc 回收?

确实有Classloader,这里是系统自带的application class loader,他会hold住所有的class Object 引用。我们尝试用自己的classloader试试。

     ClassLoader cl = null;
     for(int i = 0; i < Integer.MAX_VALUE; i++)
     {
      Enhancer enhancer = new Enhancer();
      if(i%100 == 0)
      {
       cl = null;
       cl = new MyClassLoader();
      }
      enhancer.setClassLoader(cl);
      enhancer.setUseCache(false);
      enhancer.setCallbackType(MyMethodInterceptor.class);
      enhancer.createClass();
     }

 

[Full GC [Tenured[Unloading class net.sf.cglib.empty.Object$$EnhancerByCGLIB$$62f811b3_63]

.......
[Unloading class net.sf.cglib.empty.Object$$EnhancerByCGLIB$$62f811b3_38]

: 1025K->866K(2504K), 0.0152307 secs] 1104K->866K(3080K), [Perm : 4095K->2677K(4096K)], 0.0155104 secs]

这次Full GC的时候有unload classes.

 

So let me sum up when a class is qulified to be unloaded.

 

- they will not be unloaded while any instances are still in
existence (have not been GC-ed).

- they will not be unloaded while their owning classloader is still in
instances is still in existence (has not been GC-ed).

- they will not be unloaded while their java.lang.Class object is still
referenced from anywhere (same goes for reflective access to their
members).

分享到:
评论

相关推荐

    java虚拟机

    - 永久代(Permanent Generation) 当一个对象被创建时,它首先进入新生代,之后有可能被转移到老年代中。新生代存放着大量的生命很短的对象,因此新生代在三个区域中垃圾回收的频率最高。为了更高效地进行垃圾回收...

    eclipse 内存溢出解决办法

    ermGen space的全称是Permanent Generation space,是指内存的永久保存区域OutOfMemoryError: PermGen space从表面上看就是内存益出,解决方法也一定是加大内存。说说为什么会内存益出:这一部分 用于存放Class和...

    字符数组的存储方式 字符串常量池.docx

    字符串在java程序中被大量使用,为了避免每次都创建相同的字符串对象及内存分配,JVM内部对字符串对象的创建做了一定的优化,在Permanent Generation中专门有一块区域用来存储字符串常量池(一组指针指向Heap中的...

    Tomcat内存溢出的三种情况及解决办法分析

    PermGen space的全称是Permanent Generation space,是指内存的永久保存区域。这一部分用于存放Class和Meta的信息,Class在被 Load的时候被放入PermGen space区域,它和和存放Instance的Heap区域不同,GC(Garbage ...

    Design of Brushless Permanent-Magnet Motors

    for hrush less permanent-niaghei motors ever It is dcnigncd to sent the modern computer based generation of mo;or engineers, '['he book. uoe» hand-in-hand with modern software-based tedwiquer- for ...

    Java GC的那些事(1)

    前言  与C语言不同,Java内存...为了进行高效的垃圾回收,虚拟机把堆内存划分成新生代(Young Generation)、老年代(Old Generation)和代(Permanent Generation)3个区域。  新生代  新生代由 Eden 与 S

    All Operating Region Power Control of Permanent Magnet Synchronous Wind Power Generation Systems Based on Inverse System Method

    All Operating Region Power Control of Permanent Magnet Synchronous Wind Power Generation Systems Based on Inverse System Method

    space_vector_pwm_generation.zip_permanent_pmsm_space vector_svpw

    永磁同步电机PMSM空间矢量控制SVPWM 仿真模型

    ADL5380 英文资料

    Stresses above those listed under Absolute Maximum Ratings may cause permanent damage to the device. This is a stress rating only; functional operation of the device at these or any other conditions ...

    Printed Circuits Handbook

    23.4 Next-Generation HDI Processes / 23.33 References 23.37 Part 6 Fabrication Chapter 24. Drilling Processes 24.3 24.1 Introduction / 24.3 24.2 Materials / 24.4 24.3 Machines / 24.11 24.4 Methods / ...

Global site tag (gtag.js) - Google Analytics