概述

本周有个同事过来咨询一个比较诡异的gc问题,大概现象是,系统一直在做cms gc,但是老生代一直不降下去,但是执行一次jmap -histo:live之后,也就是主动触发一次full gc之后,通过jstat -gcutil来看老生代一下就降下去了,初看下理论上不太可能,因为full gc也会对old做回收,于是我要同事针对他们的场景写了一个简单的demo出来,然后果然还真能重现,不过他的demo设置的Heap有32G,于是我通过慢慢调整,最终在很小的内存下也能重现出来

Demo

测试代码如下:

正如我上面注释里写的JVM参数,控制新生代200M,老生代300M,老生代使用率达到90%的时候触发CMS GC,大家可以跑跑看,这种情况下会发现不断做CMS GC,但是老生代就是不降下去,但是只要你主动触发一次Full GC,老生代立马就会回收。
当allocateMemory方法执行完之后,期待的结果是gc之后List及里面的byte数组都应该被回收掉,可是事实并不是这样的

初步定位

这段代码非常简单,我翻来覆去地看着这段代码,试图想改变点什么,能让问题出现峰回路转,我不断地控制for循环的次数和每次分配的内存大小,最终我将目标转移到那个ArrayList上,List里有个数组,在add过程中如果发现数组不够了,于是会进行扩容,那扩容就是创建新的数组,将老的对象放到新数组里,那我试想要是不做扩容会不会有问题?于是我开始调整ArrayList的初始化大小,当我调到一定大小,保证在add过程中不会做扩容,问题真出现了反转,居然能正常回收了,比如上面的demo,将数组长度设置为len,那结果就完全不一样了,老生代很快就被回收了
那目标能锁定到数组扩容了

数组扩容

ArrayList里的数组扩容,使用的是System.arrayCopy调用,这是一个native方法,在java层面创建一个新的长度的数组,然后将老数组和新数组都传进去,在native里将老数组里的元素指针拷贝到新数组里,其实做的是浅拷贝,反复看native这块实现,也基本解释不通那个现象,一度怀疑我对GC的理解了,是不是有哪些细节没有注意到。
经过我内存dump分析,发现上面Demo里的List对象确实被回收了,但是List里的数组没有被回收,这个数组里的byte数组都没有被回收

原来是这个鬼

带着百思不得其解的疑惑和我们组同事讨论,看看还有没有其他可能的没考虑到疑惑点,开始也都觉得疑惑,后来传胜突然想到会不会是存在跨代引用的问题,于是回过来仔细再想想每个步骤,好像还真有可能,因为传给System.arrayCopy的新数组是在java层面构建传进来的,在新生代分配的可能性最大,这样再加上拷贝仅仅是浅拷贝,那么老生代里的byte数组因为存在新生代里新数组的引用,那仅仅做CMS GC就不可能回收这些老生代的对象了,因为CMS GC的一个gc root就是新生代里的对象

那何解

至此终于抓出了那个鬼,于是想应对策略,既然这样,只要保证在cms gc回收old之前做一次ygc就能保证新生代里的那个新数组被回收而没有指向老生代那些byte数组,那么这些数组就能正常被cms gc回收了,所以加上-XX:+CMSScavengeBeforeRemark即可解此问题。

一起来学习吧:

PerfMa KO 系列课之 JVM 参数【Memory篇】

实战:OOM 后我如何分析解决的

最新文章

  1. C#开发奇技淫巧三:把dll放在不同的目录让你的程序更整洁
  2. C#与JS实现 获取指定字节长度 中英文混合字符串 的方法
  3. python MethodType方法详解和使用
  4. [转]C# dataGridview 报“索引-1没有值”的解决办法
  5. j2ee log4j集中式日志解决方案logpool v0.3
  6. 好玩儿的expect
  7. 使用app loader上传iOS应用
  8. crontab无法调用java的问题解决
  9. 使用Canvas实现下雪功能
  10. VS2010在C盘下生成的.iTrace文件解决办法 ,c盘偷偷的减少,心很烦啊,找了半天才知道是这个问题
  11. 总结下js中匿名函数的写法~好几天没写博客了。。。
  12. SAXReader简单实例解析HTML
  13. Tomcat中常见线程说明
  14. python接口自动化(十三)--cookie绕过验证码登录(详解)
  15. mysql函数调用过程
  16. 记一次vue长列表的内存性能分析和优化
  17. [物理学与PDEs]第1章习题12 Coulomb 规范下电磁场的标势、矢势满足的方程
  18. 有关vuex的问题
  19. 项目UML设计(团队)
  20. Oracle中对number类型数据to_char()出现各位少0,或者值为###的处理

热门文章

  1. CodeForces - 260C
  2. 【mybatis xml】数据层框架应用--Mybatis(三)关系映射之一对一关系映射
  3. session与cookie的浅谈
  4. Element upload组件上传图片与回显图片
  5. Composition API
  6. C - A Plug for UNIX POJ - 1087 网络流
  7. (二)Redis在Mac下的安装与SpringBoot中的配置
  8. 测试开发专题:spring-boot统一异常捕获
  9. 036_python的大文件下载以及进度条展示
  10. 使用Java实现简单的斗地主案例