jdk8之永久区Permanent区参数设置分析

原创  2016年08月03日 17:56:25

引言: JVM中的内存区域一般分为3个部分: 年轻代、年老代和永久代;永久代在JDK 7中逐渐变化,到JDK 8之后完全消失,合并到了Native堆中。本文将逐个分析其中的使用和状况。

1.   环境说明

   windows 7,  JDK 1.7.0_79/JDK 1.8.0_45.  STS

2. 何为永久区?

    主要是JVM在运行过程中,存放Class的静态信息,Main方法信息,常量信息,静态方法和变量信息,共享变量等信息。一般很少被JVM进行回收。一般的动态替换Class的行为都是在这个区域来进行的。

3.   JDK 7下的永久区参数设置

    参数设置示例: -XX:PermSize=5M -XX:MaxPermSize=7M

    说明:  PermSize为永久区大小, MaxPermSize为最大的永久区大小。

    代码示例:

       JVM参数: -XX:PermSize=5M -XX:MaxPermSize=7M -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d:\oom.dump

[html]  view plain  copy
  1. import java.lang.reflect.Method;  
  2.   
  3. import net.sf.cglib.proxy.CallbackFilter;  
  4. import net.sf.cglib.proxy.Dispatcher;  
  5. import net.sf.cglib.proxy.Enhancer;  
  6. import net.sf.cglib.proxy.MethodInterceptor;  
  7.   
  8. public class OOMTest {  
  9.   
  10.     public static void main(String[] args) {  
  11.         System.out.println("Let us do it now.....");  
  12.         for(int i=0;i<100000;i++){  
  13.            Enhancer enhancer = new Enhancer();  
  14.            enhancer.setSuperclass(BaseFlyer.class);  
  15.            enhancer.setCallbackTypes(new Class[] {  
  16.                     Dispatcher.class, MethodInterceptor.class });  
  17.            enhancer.setCallbackFilter(new CallbackFilter() {  
  18.                 public int accept(Method method) {  
  19.                     return 1;  
  20.                 }  
  21.             });  
  22.              
  23.            Class clazz = enhancer.createClass();  
  24.              
  25.            System.out.println("Time:" + System.currentTimeMillis());   
  26.         }  
  27.     }  
  28. }  
   输出结果:
[html]  view plain  copy
  1. Time:1470203973444  
  2. Time:1470203973447  
  3. Time:1470203973449  
  4. Time:1470203973452  
  5. Time:1470203973456  
  6. Time:1470203973459  
  7. java.lang.OutOfMemoryError: PermGen space  
  8. Dumping heap to d:\oom.dump ...  
  9. Unable to create d:\oom.dump: File exists  
  10. Exception in thread "main"   
  11. Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "main"  
     注意这里的关键词为:java.lang.OutOfMemoryError: PermGen space, 永久区溢出。

      基于jvisualvm来打开dump文件,可以发现defineClass的时候发生的内存溢出,另外出现了大量的BaseFlyer子类的动态类,这个导致了Perm区域的溢出。

     

      在defineClass()之时,报出溢出错误信息:

      

    在JDK 7中开始进行移除永久代的努力,下面列出了JDK7中从永久带移除的东西:

  •    符号引用被移到了native堆
  •    池化string对象被移到了java堆
  •    Class对象、静态变量被移到了java堆
       上述信息都被转移到了Java堆中了。 

4. JDK 8中的永久区设置

    JDK8中已经完全移除了永久带。这项工作是在这个bug:https://bugs.openjdk.java.net/browse/JDK-6964458推动下完成的。JDK8中,PermSize和MaxPermSize参数也一并移除了。

   在移除了Perm区域之后,JDK 8中使用MetaSpace来替代,这些空间都直接在堆上来进行分配。  在JDK8中,类的元数据存放在native堆中,这个空间被叫做:元数据区。JDK8中给元数据区添加了一些新的参数。

  •     -XX:MetaspaceSize=<NNN> <NNN>是分配给类元数据区(以字节计)的初始大小(初始高水位),超过会导致垃圾收集器卸载类。这个数量是一个估计值。当第一次到达高水位的时候,下一个高水位是由垃圾收集器来管理的。
  •    -XX:MaxMetaspaceSize=<NNN> <NNN>是分配给类元数据区的最大值(以字节计)。这个参数可以用来限制分配给类元数据区的大小。这个值也是个估计值。默认无上限。
  •   -XX:MinMetaspaceFreeRatio=<NNN>,<NNN>是一次GC以后,为了避免增加元数据区(高水位)的大小,空闲的类元数据区的容量的最小比例,不够就会导致垃圾回收。
  •   -XX:MaxMetaspaceFreeRatio=<NNN>,<NNN>是一次GC以后,为了避免减少元数据区(高水位)的大小,空闲的类元数据区的容量的最大比例,超过就会导致垃圾回收。

     默认情况下,类元数据的分配仅受限于可用的本地内存。我们可以使用新的MaxMetaspaceSize参数限定类元数据可用的本地内存的数量。它类似于MaxPermSize。当类元数据区使用量到达MetaspaceSize(32位机客户端模式12M,32位服务器模式16M,64位机会更大)的时候,会触发垃圾回收,然后回收掉无用的类加载器和class对象。      

     MetaspaceSize的值设置的过大会延长垃圾回收时间。垃圾回收过后,引起下一次垃圾回收的类元数据空间的大小可能会变大。

      代码示例:

        JVM参数设置:  -XX:MetaspaceSize=5M -XX:MaxMetaspaceSize=7M -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d:\oom.dump

         cglib依赖包:

[html]  view plain  copy
  1.               <dependency>  
  2.     <groupId>cglib</groupId>  
  3.     <artifactId>cglib</artifactId>  
  4.     <version>3.2.4</version>  
  5. </dependency>  

        代码内容:

[html]  view plain  copy
  1. import java.lang.reflect.Method;  
  2.   
  3. import net.sf.cglib.proxy.CallbackFilter;  
  4. import net.sf.cglib.proxy.Dispatcher;  
  5. import net.sf.cglib.proxy.Enhancer;  
  6. import net.sf.cglib.proxy.MethodInterceptor;  
  7.   
  8. public class OOMTest {  
  9.   
  10.     public static void main(String[] args) {  
  11.         System.out.println("Let us do it now.....");  
  12.         for(int i=0;i<100000;i++){  
  13.            Enhancer enhancer = new Enhancer();  
  14.            enhancer.setSuperclass(BaseFlyer.class);  
  15.            enhancer.setCallbackTypes(new Class[] {  
  16.                     Dispatcher.class, MethodInterceptor.class });  
  17.            enhancer.setCallbackFilter(new CallbackFilter() {  
  18.                 public int accept(Method method) {  
  19.                     return 1;  
  20.                 }  
  21.             });  
  22.              
  23.            Class clazz = enhancer.createClass();  
  24.              
  25.            System.out.println("Time:" + System.currentTimeMillis());   
  26.         }  
  27.     }  
  28. }  
      运行结果:       
[html]  view plain  copy
  1. Time:1470217316712  
  2. Time:1470217316716  
  3. Time:1470217316718  
  4. Time:1470217316721  
  5. Time:1470217316724  
  6. Time:1470217316727  
  7. Time:1470217316730  
  8. java.lang.OutOfMemoryError: Metaspace  
  9. Dumping heap to d:\oom.dump ...  
  10. Heap dump file created [2577942 bytes in 0.022 secs]  
  11. Exception in thread "main" java.lang.IllegalStateException: Unable to load cache item  
  12.     at net.sf.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:79)  
  13.     at net.sf.cglib.core.internal.LoadingCache.get(LoadingCache.java:34)  
  14.     at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:116)  
  15.     at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:291)  
  16.     at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:480)  
  17.     at net.sf.cglib.proxy.Enhancer.createClass(Enhancer.java:337)  
  18.     at org.homework.test.jvm.jvmopt.OOMTest.main(OOMTest.java:25)  
  19. Caused by: java.lang.OutOfMemoryError: Metaspace  
  20.     at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:345)  
  21.     at net.sf.cglib.proxy.Enhancer.generate(Enhancer.java:492)  
  22.     at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:93)  
  23.     at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:91)  
  24.     at net.sf.cglib.core.internal.LoadingCache$2.call(LoadingCache.java:54)  
  25.     at java.util.concurrent.FutureTask.run(Unknown Source)  
  26.     at net.sf.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:61)  
  27.     ... 6 more  

   分析错误日志,发现Metaspace是核心关键词,如果发现类似的关键词,即可确定是Metaspace的设置问题。

    基于jvisualvm来进行dump文件分析的结果与JDK 7中的结果类似,这里不再重复赘述,各位可以自行研究分析。

4.  G1垃圾回收算法

    G1是在JDK 7中推出的新GC算法,相比之前的JVM内存模型,这是一个巨大的飞跃,慢慢打破了不同代系之间的阻隔。   G1和永久区的逐步消失是密切耦合的。

      在G1中,堆被划分成 许多个连续的区域(region)。每个区域大小相等,在1M~32M之间。JVM最多支持2000个区域,可推算G1能支持的最大内存为2000*32M=62.5G。区域(region)的大小在JVM初始化的时候决定,也可以用-XX:G1HeapReginSize设置。

      在G1中没有物理上的Yong(Eden/Survivor)/Old Generation,它们是逻辑的,使用一些非连续的区域(Region)组成的。
     G1虽然保留了CMS关于代的概念,但是代已经不是物理上连续区域,而是一个逻辑的概念。在标记过程中,每个区域的对象活性都被计算,在回收时候,就可以根据用户设置的停顿时间,选择活性较低的区域收集,这样既能保证垃圾回收,又能保证停顿时间,而且也不会降低太多的吞吐量。Remark阶段新算法的运用,以及收集过程中的压缩,都弥补了CMS不足。引用Oracle官网的一句话:“G1 is planned as the long term replacement for the Concurrent Mark-Sweep Collector (CMS)”。

      这里先简要提一下G1的垃圾回收算法,稍后我还会专门描述G1的回收算法。

5.  总结

     在这里我们分别在JDk 7和JDK 8针对永久区进行了分析,这里需要着重强调的是两者之间是无法彼此兼容的设置,各自自行设置调整。


      

     http://blog.csdn.net/blueheart20/article/details/52103020


  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值