关于系统GC的疑问?

悟性 发布于 2013/08/12 12:52
阅读 799
收藏 0

背景:最近系统中存在着内存问题,我们的MapReduce程序在本地运行内存使用正常,通过软件监测也不存在内存泄露。但是一旦在集群上运行,就会内存持续增长。Map Task偶尔后因为内存使用超过配置被Yarn强制杀掉。同样的数据再启动一次就正常了。

我们就想自己控制JVM的GC频率,通过下面代码测试:

public static void main(String[] args) {
 
  Runtime runtime = Runtime.getRuntime();
 
  MemoryMXBean mbean = ManagementFactory.getMemoryMXBean();
 
  mbean.setVerbose(true);
 
  HashMap map = new HashMap();
 
  int j = 1;
 
  for(long i=0L;i<1000000;i++)
 
  {
 
   if(j>100)
 
   {
 
    j =1;
 
   }
 
   for(int k=0; k< j; k++)
 
   {
 
    String str = i + "," + k +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
 
    map.put(i+","+k, str);
 
   }
   long mem = runtime.totalMemory()/(1024*1024);
 
   if(mem > 200)
 
   {
 
    map = null;
 
    
    runtime.runFinalization();
 
    runtime.gc();
 
    try {
 
     Thread.sleep(1000);
 
    } catch (InterruptedException e) {
 
    }
 
    System.out.println("----------------------------------------------");
 
    map = new HashMap();
 
   }
 
  }
 
}
运行结果:
----------------------------------------------
 
[GC 4627K->169K(371712K), 0.0001675 secs]
 
[Full GC 169K->169K(371712K), 0.0064708 secs]
 
----------------------------------------------
 
[GC 4627K->169K(371712K), 0.0000810 secs]
 
[Full GC 169K->169K(371712K), 0.0044233 secs]
 
----------------------------------------------
这里存在困惑,为什么Minor GC之后,紧跟了个Full GC。而且内存已经在第一次Minor GC时已经释放,Full GC内存变化不大,但是Full GC的耗时确实第一次Minor GC的40多倍。这个开销太不值得了,有什么方法只执行第一Minor GC,而不是Full GC?

以下是问题补充:

@悟性:补充:问题背景不是说本身程序出现OutOfMemory异常,而是被调度进程Yarn发现总内存超过配置的最大内存1.1G,强制被Yarn杀掉。我们解决思路是认为既然没有内存泄露,那么应该可以GC。所以想在起个定时器,定时监测内存是否达到某个阙值,那么就执行系统GC,释放内存。这样就不会被Yarn调度进程杀掉。我的问题是能不能只做到Minor GC?另外有高手解答这个分布式的内存问题如何定位,当然求之不得。 (2013/08/12 19:00)
加载中
0
x
xsong
先了解什么是 full gc, 为什么会 full gc,  什么时候会执行. 这个涉及到 jvm 的内存分布以及一些gc的策略 , 还有代码质量的问题. 有些复杂. 
whaon
whaon
求大大解答
0
悟性
悟性
System.gc()就是Full GC。难道Full GC是分步执行的?先执行Minor GC,再执行Full GC的动作?现在就是觉得Full GC的时间开销有点多,能否做到只执行Minor GC?
0
专业打酱油
专业打酱油

1、-XX:+PrintGCDetails

2、看下详细信息

3、亲,你起码得列出JVM参数吧

4、Full GC发生和Minor GC没什么关系,只要满足Full GC触发的条件,就会发生。

    对象需要放到old中,old没有空间了,就会发生。至于什么时候会把对象放入到old中?根据策略配置,可能对象足够大,比如超过1M,直接进去old,比如eden对象回收很多次仍然存在,超过5次仍然存过,放入old,比如对象放入eden,发现eden放不下了,直接放入eden等等(1,5都是随便说的)

5、我这智商看不懂你的代码,但是运行没发现什么问题。。

6、等高手回答

0
huan
huan
这种问题最好找到根源所在,否则这种头疼医头的方式虽然可能解决眼前问题,但是会带来潜在风险。 而且手动调用gc方法是极不推荐的。
0
悟性
悟性

引用来自“huan”的答案

这种问题最好找到根源所在,否则这种头疼医头的方式虽然可能解决眼前问题,但是会带来潜在风险。 而且手动调用gc方法是极不推荐的。
请教大虾,有啥好的定位思路?
0
whaon
whaon
你把 runtime.gc();去了,看还有么
0
huan
huan

引用来自“悟性”的答案

引用来自“huan”的答案

这种问题最好找到根源所在,否则这种头疼医头的方式虽然可能解决眼前问题,但是会带来潜在风险。 而且手动调用gc方法是极不推荐的。
请教大虾,有啥好的定位思路?
既然在集群上运行会出现异常,那就在集群上调试。jconsole 和jvisualvm 等工具都支持远程监控。实在不行就把堆 dump出来慢慢分析。
0
悟性
悟性

引用来自“huan”的答案

引用来自“悟性”的答案

引用来自“huan”的答案

这种问题最好找到根源所在,否则这种头疼医头的方式虽然可能解决眼前问题,但是会带来潜在风险。 而且手动调用gc方法是极不推荐的。
请教大虾,有啥好的定位思路?
既然在集群上运行会出现异常,那就在集群上调试。jconsole 和jvisualvm 等工具都支持远程监控。实在不行就把堆 dump出来慢慢分析。

这两个都有使用。

1. jvisualvm 远程监控无法提供内存抽样。无法定位到哪个类的问题。本地的是可以监测,我们观察到没有内存泄露,系统周期性回收内存,很有规律。

2. jconsole问题:因为是Yarn自动调度的Map Task,且不知道程序固定失败点,所以无法直接定位到哪个进程,jconsole不方便使用,而且同样存在无法定位到哪个类的问题。

不知道你说的dump内存是否使用,-XX:HeapDumpPath=./dump.core 这个参数?

0
乾坤摄
乾坤摄
唉,内存膨胀是因为你没有设置,虚拟机的最大内存,如果虚拟机的最小内存过小的话,服务器启动后会进行多次full gc 在扩容jvm内存,所以减少full gc首先现把虚拟机最小内存设置大,你可以查一下,好像说虚拟机有办法找到最合理的内存大小
0
乾坤摄
乾坤摄
关于服务内存,占用过大也有一种可能是虚拟机32位和64位的差异,64位中对象地址会扩大一倍
返回顶部
顶部