009|分析一次JVM多久会发生GC
分析的案例背景:
公司研发的一个数据计算系统,日处理数据量在上亿的规模。是一个分布式部署的系统,
- 每台机器大概每分钟执行100次数据提取和计算
- 每次大概需要计算10秒
- 每次大概提取1万条左右的数据到内存里面
机器的配置:
4核8G,JVM 内存给4G,其中新生代和老年代分别是 1.5G的内存空间。如下图,上面的图
那么新生代多久会被塞满?
假设,每条数据平均10个字段,可以认为每条数据在1KB 左右的大小,那么每次计算任务的一万条数据就差不多 10M 左右。
按照 8:1:1 的比例来分配 Eden 和 Survivor 区域,那么 Eden 1.2G,Survivor 大概在 100M 左右。
按照上面的内存大小而言,每次计算分配10M,一分钟大概100次左右,就是1G,基本是上一分钟左右就会被填满,
此时,假设1分钟过后,都塞满来对象,就会导致 Minor GC 回收垃圾,我们知道 回收之前 会看看老年代是否大于新生代,此时老年代还有 1.5G,完全放得下,就会执行 Minor GC;
假设,每个计算任务需要执行10秒,假设此时 80个已经执行完,还有20个,那么存活的就是 200M,然后有1G 的垃圾,此时一次 Minor GC 回收掉 1G,但是 200M的存活却不能放入 Surivor 区,因为任何一块,都只有100M,此时 通过空间担保机制就会直接放入老年代。如下图 (上)
那么 老年代大概多久会被填满呢?
假设 2分钟过去,老年代 就有400M垃圾,此时 只有1.1G,
第三分钟运行完毕,此时 1.1G < 1.2G ,就会去看 “-XX:-HandlePromotionFailure” 参数是否被设置,被打开,就会进一步检测,是否大于 历次的平均大小,大于就会进行 Minor GC。
7分钟后,大概有1.4G进入老年代,老年代剩余不到 100M,
8分钟后,会触发 Full GC。清除垃圾,200M进入老年代,所以综上,7,8 分钟就会触发一次,Full GC。
那么如何优化?
从上面的分析我们知道,200M直接进入老年代是因为,Survivor 太小放不下,所以,我们只要调大 Survivor区。
即,3G左右的堆内存,2G给新生代,1G给老年代即可。这样 Survivor 200M就可以放下,这样就很少对象会进入老年代,这样就减少了Full GC.