前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >简述 G1 垃圾回收器和 OOM 问题的排查

简述 G1 垃圾回收器和 OOM 问题的排查

作者头像
付威
发布2021-01-28 13:09:48
2.6K0
发布2021-01-28 13:09:48
举报
文章被收录于专栏:老付的网络博客

最近又碰到的 oom 的问题,一直在尝试定位中,由于现实使用的 G1 的垃圾回收器。所以今天打算线上的排查历程和方案查询出来。

jvm 常用参数
代码语言:javascript
复制
-Xmx1024m 最大堆内存
-Xms1024m 最小堆内存
-Xss256k  设置栈的大小。栈都是每个线程独有一个,所有一般都是几百k的大小。
-XX:MetaspaceSize=128m  元空间的大小
-XX:MaxMetaspaceSize=256m  最大元空间大小
-XX:MaxGCPauseMillis=200 设置每次年轻代垃圾回收的最长时间,如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值
-XX:+UseG1GC 使用 G1 垃圾回收器
-XX:-OmitStackTraceInFastThrow 当一些异常在代码里某个特定位置被抛出很多次的话,HotSpot Server Compiler(C2)会用fast throw来优化这个抛出异常的地方。
-XX:MinHeapFreeRatio=30
-XX:MaxHeapFreeRatio=50
-XX:MaxDirectMemorySize=100M 直接内存大小
-XX:+PrintGCDetails 打印 GC 详细信息
-XX:+DisableExplicitGC 禁止显示GC
几个命令

在排查的过程中用到的下面几个命令

代码语言:javascript
复制
jmap
pmap 命令
perf 命令
内存 RSS、VSZ的区别

出现 OOM 的问题,一般情况下来说,都是堆上面内存分配的太多,且无法回收,导致 JVM 的内存溢出。

jps

jmap -heap pid

jmap -histo pid 可以查看对应的类型的大小,或者使用 dump 成一个文件进行分析

在对堆上的类型对象进行分析的时候,发现堆上的内存大小和回收的基本正常,实际使用的内存是大于堆上的内存, 这个时候我就开始怀疑是堆外内存的泄露的问题。

使用 pmap -x pid | sort -n -k3 指令,看下占用内存的地址空间和大小

前面的工具如果再无法定位问题的话,就只能使用 命令,基本就两条语句

perf record -g -p pid

执行perf report -i perf.data查看报告。

根据查看的内容,定位到 zip 的内容,但是内容还是不大,也 review zip 相关代码,进行本地测试,均为发生 OOM 的现象。

前面的几个办法都无法定位问题,只能使用最笨的办法,打印直接内存的大小。具体的代码如下:

代码语言:javascript
复制
     public BufferPoolMXBean getDirectBufferPoolMBean(){
         return ManagementFactory.getPlatformMXBeans(BufferPoolMXBean.class)
                 .stream()
                 .filter(e -> e.getName().equals("direct"))
                 .findFirst()
                 .orElseThrow(null);
     }

     public JavaNioAccess.BufferPool getNioBufferPool(){
         return SharedSecrets.getJavaNioAccess().getDirectBufferPool();
     }

     /**
         * -XX:MaxDirectMemorySize=60M
         */
     @Test
     public void testGetMaxDirectMemory(){
         ByteBuffer.allocateDirect(25*1024*1024);
         System.out.println(Runtime.getRuntime().maxMemory() / 1024.0 / 1024.0);
         System.out.println(VM.maxDirectMemory() / 1024.0 / 1024.0);
         System.out.println(getDirectBufferPoolMBean().getTotalCapacity() / 1024.0 / 1024.0);
         System.out.println(getNioBufferPool().getTotalCapacity() / 1024.0 / 1024.0);
     }
G1 回收器的特点

G1 不同于其他的分代回收算法、G1将堆空间划分成了互相独立的区块。每块区域既有可能属于 Old 区、也有可能是 Y 区,且每类区域空间可以是不连续的(对比 CMS 的 O 区和 Y 区都必须是连续的)。

这种将O区划分成多块的理念源于:当并发后台线程寻找可回收的对象时、有些区块包含可回收的对象要比其他区块多很多。虽然在清理这些区块时G1仍然需要暂停应用线程、但可以用相对较少的时间优先回收包含垃圾最多区块。这也是为什么G1命名为Garbage First的原因:第一时间处理垃圾最多的区块。

所以在看到 Old 区发生了变化,但是没有发生 Full GC。这个就是 G1 的 mixed 垃圾回收回收的。

这是一篇水文,随便写写。

[参考资料] 【深入理解G1垃圾收集器】 【Java堆外内存排查小结】 【聊聊jvm的-XX:MaxDirectMemorySize

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-09-122,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • jvm 常用参数
  • 几个命令
  • G1 回收器的特点
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档