又抓了一个导致频繁GC的鬼--数组动态扩容
概述
本周有个同事过来咨询一个比较诡异的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篇】
又抓了一个导致频繁GC的鬼--数组动态扩容的更多相关文章
- 频繁GC会造成卡顿
频繁GC会造成卡顿 https://www.cnblogs.com/qcloud1001/p/9525078.html 一款app除了要有令人惊叹的功能和令人发指交互之外,在性能上也应该追求丝滑的要求 ...
- 面试被问怎么排查平时遇到的系统CPU飙高和频繁GC,该怎么回答?
处理过线上问题的同学基本上都会遇到系统突然运行缓慢,CPU 100%,以及Full GC次数过多的问题.当然,这些问题的最终导致的直观现象就是系统运行缓慢,并且有大量的报警.本文主要针对系统运行缓慢这 ...
- 生产环境碰到系统CPU飙高和频繁GC系统反应慢,你要怎么排查?(转)
处理过线上问题的同学基本上都会遇到系统突然运行缓慢,CPU 100%,以及Full GC次数过多的问题.当然,这些问题的最终导致的直观现象就是系统运行缓慢,并且有大量的报警.本文主要针对系统运行缓慢这 ...
- CPU飙高,频繁GC,怎么排查?
处理过线上问题的同学基本上都会遇到系统突然运行缓慢,CPU 100%,以及Full GC次数过多的问题.当然,这些问题的最终导致的直观现象就是系统运行缓慢,并且有大量的报警. 本文主要针对系统运行缓慢 ...
- 平时碰到系统CPU飙高和频繁GC,你会怎么排查?
处理过线上问题的同学基本上都会遇到系统突然运行缓慢,CPU 100%,以及Full GC次数过多的问题.当然,这些问题的最终导致的直观现象就是系统运行缓慢,并且有大量的报警.本文主要针对系统运行缓慢这 ...
- FGC频繁 GC卡顿
https://mp.weixin.qq.com/s/I1fp89Ib2Na1-vjmjSpsjQ 线上服务的FGC问题排查,看这篇就够了! 原创 骆俊武 IT人的职场进阶 2020-05-10 ...
- 故障重现(内存篇2),JAVA内存不足导致频繁回收和swap引起的性能问题
背景起因: 记起以前的另一次也是关于内存的调优分享下 有个系统平时运行非常稳定运行(没经历过大并发考验),然而在一次活动后,人数并发一上来后,系统开始卡. 我按经验开始调优,在每个关键步骤的加入如 ...
- JS垃圾回收——和其他语言一样,JavaScript 的 GC 策略也无法避免一个问题:GC 时,停止响应其他操作,这是为了安全考虑
JavaScript 内存管理 & 垃圾回收机制 标记清除 js 中最常用的垃圾回收方式就是标记清除.当变量进入环境时,例如,在函数中声明一个变量,就将这个而变量标记为“进入环境”.从逻辑上讲 ...
- ########django-基于中间件写一个限制频繁登陆########
django-基于中间件写一个限制频繁登陆 额额,标题已经很醒目了,通过中间件去实现,其他方法也可以实现 浏览器前端传来的请求,必须通过中间件,才能到后面路由,视图函数,所以我们在中间件那里做一层处理 ...
随机推荐
- 学数据库你竟然不用用JAVA写代码,可惜你遇到了我! JAVA连接数据库(JDBC)的安装使用教程
Step 1 你得有Eclipse 没有出门右拐,我教不了你. Step 2 你得有Mysql MySQL的详细安装过程,我在另一篇博客中给出.戳我 Step 3 安装JDBC 可以去官网下,如果用的 ...
- 基于UDP的客户端和服务器端的代码设计
实验平台 linux 实验内容 编写UDP服务器和客户端程序,客户端发送消息,服务器接收消息,并打印客户端的IP地址和端口号. 实验原理 UDP是无需连接的通信,其主要实现过程如下: 同样,我们可以按 ...
- 【K8S】K8S 1.18.2安装dashboard(基于kubernetes-dashboard 2.0.0版本)
[K8S]K8S 1.18.2安装dashboard(基于kubernetes-dashboard 2.0.0版本) 写在前面 K8S集群部署成功了,如何对集群进行可视化管理呢?别着急,接下来,我们一 ...
- nginx常见应用实例
1.nginx 中 location 应用实例location 是 nginx 的精华,nginx 就是通过拦截到的请求去对配置好的 location 块(location block)进行请求代理的 ...
- IDEA破解,自动激活【2020年版本也可以破解】
破解 在破解之前,需要下载的文件有两个ideaIU-2019.3.3.exe.jetbrains-agent-latest.zip,前者是程序安装包,后者是破解补丁包(PS:补丁包里面有使用说明,下面 ...
- 简单谈谈Spring的IoC
一.前言 这几天正在复习Spring的相关内容,同时想要对Spring的实现原理做一些深入的研究.今天看了看Spring中IoC的实现,找到了一篇非常详细的博客,研究了一个下午,看完之后唯一的感受 ...
- layui里面的layer模块弹窗,强制居中的方法!!!
每次调用layer弹窗做动态展示的时候,只要内容不固定或者需要二次渲染 比如layui.form.render()进行渲染的时候 由于弹窗已经出来了,只是content部分的结构改变了宽度或者高度,l ...
- Linux内核驱动学习(九)GPIO外部输入的处理
文章目录 前言 设备树 两个结构体 gpio_platform_data gpio_demo_device 两种方式 轮询 外部中断 总结 附录 前言 前面是如何操作GPIO进行输出,这里我重新实现了 ...
- Python拆分一列为多列
有的员工,没有公司开户行的银行卡,发放现金工资.有时人多,需要计算币数.现金工资表中,其中一列为实发工资,import pandas as pd,转化为pd.DataFrame. 面值[100,50, ...
- 在本地运行Kubernetes的3种主流方式
作者简介 Chris Tozzi,曾担任记者和Linux管理员.对开源技术.敏捷基础架构以及网络问题兴趣浓厚.目前担任高级内容编辑,并且是Fixate IO的DevOps分析师. 原文链接: http ...