UseAdaptiveSizePolicy与CMS垃圾回收同时使用导致的JVM报错

   系统在灰度环境上变更时发现JVM启动报错,详细检查JVM配置参数,发现新境了如下配置:

   -XX:+UseAdaptiveSizePolicy和-XX:+UseConcMarkSweepGC

   初步猜想是JVM参数配置的问题,于是通过jmap -heap查看系统堆栈使用情况,如下:

Heap Configuration:  
   MinHeapFreeRatio = 40  
   MaxHeapFreeRatio = 70  
   MaxHeapSize      = 10737418240 (10240.0MB)  
   NewSize          = 2147483648 (2048.0MB)  
   MaxNewSize       = 2147483648 (2048.0MB)  
   OldSize          = 5439488 (5.1875MB)  
   NewRatio         = 2  
   SurvivorRatio    = 4  
   PermSize         = 21757952 (20.75MB)  
   MaxPermSize      = 134217728 (128.0MB)  

Heap Usage:  
unknown generation type:  
   capacity = 0 (0.0MB)  
   used     = 0 (0.0MB)  
   free     = 0 (0.0MB)  
   NaN% used  
unknown generation type:  
   capacity = 0 (0.0MB)  
   used     = 0 (0.0MB)  
   free     = 0 (0.0MB)  
   NaN% used  
Perm Generation:  
   capacity = 60878848 (58.05859375MB)  
   used     = 37927152 (36.17015075683594MB)  
   free     = 22951696 (21.888442993164062MB)  
   62.29939173619054% used 

从打印的堆栈信息上看已发现异常

一、JVM分析

1、源码查看

  分析jdk的management.cpp代码,发现在计算堆内存区大小的时候,对commit都进行了累加,但是在max_size没有定义(无效,从MemoryPool获取)的情况下,total_max没有累加,导致commited比max大。

  修复后的代码见:  http://hg.openjdk.java.net/hsx/hsx25/hotspot/file/a70566600baf/src/share/vm/services/management.cpp的方法JVM_ENTRY中。 

 对比: 
   原来只处理: 

if (!has_undefined_max_size) {  
  total_max += u.max_size();  
}  

修复该问题的方式:增加代码处理没有定义init和max的情况  

if (has_undefined_init_size) {  
     total_init = (size_t)-1;  
  }  
if (has_undefined_max_size) {  
      total_max = (size_t)-1;  
  }  


 2、jmap不能获取数据原因

  jmap出现不能获取的原因:(UseAdaptiveSizePolicy + CMS同时使用会出现) (该状况源码: sun/jvm/hotspot/memory/GenerationFactory.java ):  

try {  
     return (Generation) ctor.instantiateWrapperFor(addr);  
} catch (WrongTypeException e) {  
     return new Generation(addr) {  
     public String name() {  
     return "unknown generation type";  
……  

 二、问题产生原因

    目前确认是jvm的bug,初步确认版本为1.6_u30以上,包括1.7都存在该问题.jdk6.30以下版本还未确认(使用1.6_u25版本后,目前还没有复现问题)---1.6.30以上到1.7的全部版本已经确认有该问题,jdk8修复,其他版本待验证

   简要原因分析请参见: http://blog.csdn.net/axman/article/details/8667351       

   在使用cms算法下,如果开启参数UseAdaptiveSizePolicy,则每次minor gc后会重新计算eden,from和to的大小,计算过程依据的是gc过程统计的一些数据,计算后的eden+from+to不会超过Xmx,同时from和to一般是不相等(初始化的时候from和to是相等的)。主要问题在于计算完后,如果eden变大,ContiguousSpacePool里面的max_eden_size并没有被更新,还是最开始时候的值,这样导致jvm在通过call_special调用java.lang.management. MemoryUsage的构造函数的时候会产生exception,产生exception的原因是eden的committed 大于 eden的max_size,导致返回java.lang.management. MemoryUsage对象失败,最终导致产生显示异常。 

三、解决办法

  可以先设置 –XX:-UseAdaptiveSizePolicy来workaround,JDK的版本:sun jdk出问题后的版本目前看是只有jdk8修复;openjdk是hs25修复。 

posted @ 2016-08-08 22:05  moonandstar08  阅读(...)  评论(...编辑  收藏