出大事了,涛哥你们Java应用GC后不释放内存
你知道的越多,不知道的就越多,业余的像一棵小草!
成功路上并不拥挤,因为坚持的人不多。
编辑:业余草
blog.csdn.net/qq_40378034
推荐:https://www.xttblog.com/?p=5221
前言
公司众多系统中有一个系统使用的是 CMS 垃圾回收器,JVM 初始堆内存不等于最大堆内存,但通过监控信息发现:在经过一次 FullGC 之后,服务器物理内存剩余空间并未提升,运维同事告诉我说,有内存泄露,因为 GC 了之后,内存并没有被释放。按照大部分人的理解,FullGC 之后 JVM 进程会释放的内存一部分还给物理内存,下面通过几个实验来对比验证一下 CMS 和 G1 的物理内存归还机制。
测试代码
public class MemoryRecycleTest {static volatile List<OOMobject> list = new ArrayList<>();public static void main(String[] args) {//指定要生产的对象大小为512Mint count = 512;//新建一条线程,负责生产对象new Thread(() -> {try {for (int i = 1; i <= 10; i++) {System.out.println(String.format("第%s次生产%s大小的对象", i, count));addObject(list, count);//休眠40秒Thread.sleep(i * 10000);}} catch (InterruptedException e) {e.printStackTrace();}}).start();//新建一条线程,负责清理List,回收JVM内存new Thread(() -> {for (; ; ) {//当List内存到达512M,就通知GC回收堆if (list.size() >= count) {System.out.println("清理list.... 回收jvm内存....");list.clear();//通知GC回收System.gc();//打印堆内存信息printJvmMemoryInfo();}}}).start();//阻止程序退出try {Thread.currentThread().join();} catch (InterruptedException e) {e.printStackTrace();}}public static void addObject(List<OOMobject> list, int count) {for (int i = 0; i < count; i++) {OOMobject ooMobject = new OOMobject();//向List添加一个1M的对象list.add(ooMobject);try {//休眠100毫秒Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}public static class OOMobject {//生成1M的对象private byte[] bytes = new byte[1024 * 1024];}public static void printJvmMemoryInfo() {//虚拟机级内存情况查询long vmFree = 0;long vmUse = 0;long vmTotal = 0;long vmMax = 0;int byteToMb = 1024 * 1024;Runtime rt = Runtime.getRuntime();vmTotal = rt.totalMemory() / byteToMb;vmFree = rt.freeMemory() / byteToMb;vmMax = rt.maxMemory() / byteToMb;vmUse = vmTotal - vmFree;System.out.println("");System.out.println("JVM内存已用的空间为:" + vmUse + " MB");System.out.println("JVM内存的空闲空间为:" + vmFree + " MB");System.out.println("JVM总内存空间为:" + vmTotal + " MB");System.out.println("JVM总内存最大堆空间为:" + vmMax + " MB");System.out.println("");}
}
JDK8 CMS
「JVM参数」:
-Xms128M -Xmx2048M -XX:+UseConcMarkSweepGC
「控制台打印的内容」:
第1次生产512大小的对象
清理list.... 回收jvm内存....JVM内存已用的空间为:6 MB
JVM内存的空闲空间为:1202 MB
JVM总内存空间为:1208 MB
JVM总内存最大堆空间为:1979 MB第2次生产512大小的对象
清理list.... 回收jvm内存....JVM内存已用的空间为:3 MB
JVM内存的空闲空间为:1097 MB
JVM总内存空间为:1100 MB
JVM总内存最大堆空间为:1979 MB第3次生产512大小的对象
清理list.... 回收jvm内存....JVM内存已用的空间为:3 MB
JVM内存的空闲空间为:706 MB
JVM总内存空间为:709 MB
JVM总内存最大堆空间为:1979 MB第4次生产512大小的对象
清理list.... 回收jvm内存....JVM内存已用的空间为:3 MB
JVM内存的空闲空间为:120 MB
JVM总内存空间为:123 MB
JVM总内存最大堆空间为:1979 MB
「VisualVM监控的堆内存情况」:
从图中堆内存的情况可以看出,在 JDK8 + CMS 的配置下,JVM 并不是立马归还内存给到操作系统,而是随着 FullGC 次数的增多逐渐归还,最终会全部归还
JDK8 G1
「JVM参数」:
-Xms128M -Xmx2048M -XX:+UseG1GC
「VisualVM监控的堆内存情况」:
在 JDK8 + G1 的配置下,JVM 都是在每一次 FullGC 后全部归还物理内存。
JDK11 CMS
「JVM参数」:
-Xms128M -Xmx2048M -XX:+UseConcMarkSweepGC
「VisualVM监控的堆内存情况」:
在 JDK11 + CMS 的配置下和 JDK8 + CMS 的情况相同(JVM 并不是立马归还内存给到操作系统,而是随着 FullGC 次数的增多逐渐归还,最终会全部归还)
JDK11 提供了一个 JVM 参数ShrinkHeapInSteps
。通过这个参数,可以在 GC 之后渐进式的归还内存给到操作系统。JDK11 下,此参数默认开启。可以把此参数关闭,看下堆内存的变化情况:
-Xms128M -Xmx2048M -XX:+UseConcMarkSweepGC -XX:-ShrinkHeapInSteps
「VisualVM监控的堆内存情况」:
在 JDK11 + CMS 的配置下,关闭ShrinkHeapInSteps
参数后,JVM 都是在每一次 FullGC 后全部归还物理内存。
JDK11 G1
由于 JDK11 默认使用的是 G1 垃圾回收器,所以这里只设置了初始堆内存和最大堆内存。
「JVM参数」:
-Xms128M -Xmx2048M
「VisualVM监控的堆内存情况」:
JDK11 默认的
ShrinkHeapInSteps
是默认开启的,但这里看堆内存变化并不是渐进的缩小的。所以在 G1 回收器下,ShrinkHeapInSteps
是无效的。如果我们手动关闭ShrinkHeapInSteps
参数,发现堆内存变化和上面这个类似JDK11 下的 G1 和 JDK8 下的 G1 对内存的响应是不一样的。从堆内存变化来看, 「JDK11 下 G1 更加倾向于尽可能的利用内存,不着急回收」。而 JDK8 下 G1 则是倾向于尽可能的先回收内存。从图中看,JDK8 下 G1 的实际使用的堆内存大小基本是 JDK11 下 G1 的一半。
小结
如果代码保持不变,但是JVM参数中设置Xms和Xmx相同的话,不管是否有FullGC,堆内存大小都不发生变化,也就不释放内存给操作系统
GC 后如何归还内存给操作系统:
能不能归还,主要依赖于 Xms 和 Xmx 是否相等
何时归还,主要依赖于 JDK 版本和垃圾回收器类型
只有 FullGC 的时候才能真正触发堆内存收缩归还 OS。YGC 是不能使 JVM 主动归还内存给操作系统的。
尽量保持 Xms 和 Xmx 一致,这样可以减少堆内存调整带来的性能损耗,也可以减少堆内存调整带来的无内存风险。
参考:
https://segmentfault.com/a/1190000019856974
https://www.cnblogs.com/androidsuperman/p/11743103.html
http://blog.dutycode.com
出大事了,涛哥你们Java应用GC后不释放内存相关推荐
- java一个接口执行结束释放内存_java的灵魂--JVM虚拟机
JVM是运行在操作系统之上的,它与硬件没有直接的交互 JVM体系结构 1.类加载器 负责加载class文件,class文件在文件开头有特定的文件标示, 并且ClassLoader只负责class文件的 ...
- java程序启动后就进行了7次younggc_程序员如何优化 Java GC
本文由CrowHawk翻译 Sangmin Lee发表在Cubrid上的"Become a Java GC Expert"系列文章的第三篇<How to Tune Java ...
- 深入理解java虚拟机gc_jvm GC收集器与内存分配(深入理解java虚拟机第三章)
jvm GC收集器与内存分配(深入理解java虚拟机第三章) 本篇是<深入理解java虚拟机第三章>的笔记记录. 一 为什么要关注GC和内存分配? 需要排查各种内存溢出.内存泄漏问题时,或 ...
- java查看gc日志_GC日志查看
GC日志查看 可以通过在java命令种加入参数来指定对应的gc类型,打印gc日志信息并输出至文件等策略. GC的日志是以替换的方式(>)写入的,而不是追加(>>),如果下次写入到同一 ...
- JAVA垃圾回收器源码_浅谈关于Java的GC垃圾回收器的一些基本概念
一.基本回收算法 1. 引用计数(Reference Counting) 比较古老的回收算法.原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一个计数.垃圾回收时,只用收集计数为0的对象.此 ...
- 卧槽!终于知道涛哥我为啥赚不到钱了
前几天和一个Java公众号头部大佬D在微信上面聊天,大佬说周末有时间约下一起聊聊,向我请教下,我以为只是一句客套话也没在意,因为一来他的公众号比我做的好,粉丝量比我多很多,其次他也有团队合作. 结果周 ...
- 跟涛哥一起学嵌入式 25:我接触过近50块嵌入式开发板,分享一下教训和总结...
最近老是有学员问我:涛哥,你的嵌入式教程针对哪款开发板鸭?到底使用哪款开发板学习?我的回答往往是:暂时不用开发板,学习嵌入式,不一定非得用开发板,在qemu仿真平台上学习就可以了.3年前我发布了互联网 ...
- Python爬虫从入门到精通:(41)JS逆向_RSA加密,Steam加密逆向_Python涛哥
RSA加密: RSA加密算法是一种非对称加密算法.在公开密钥加密和电子商业中RSA被广泛使用. 非对称加密算法: 非对称加密算法需要两个密钥: 公开密钥(publickey:简称公钥) 私有密钥(pr ...
- Python爬虫从入门到精通:(43)JS逆向:完美世界RAS逆向_Python涛哥
完美世界有很多游戏,比较出名的就是诛仙.魔兽世界等.完美今天就来看下完美世界登陆的逆向分析! JS逆向解析 登陆的时候这里有个点击认证.没关系,完美先忽略它,直接点击.完美主要探讨密码的逆向方式. 抓 ...
最新文章
- 杭电oj1176,2084java实现
- 【DSP开发】【VS开发】YUV与RGB格式转换
- ImportError: cannot import name cbook
- android 设计qq界面,[考试求助]仿照腾讯QQ应用软件,编写Android程序,登录界面设计...
- hadoop关键进程
- WINDOWS XP SP2 NTFS EFS加密文件的解密案例
- 如何处理VMware启动虚拟机时的错误信息Failed to lock the file
- Windows消息队列一
- ZZULIOJ 1090: 整数幂(多实例测试)
- 星辰网址缩短源码支持二维码
- Python 下载哔哩哔哩视频
- IP-SAN和FC-SAN存储的区别
- 媒体应用视频超分AI神器!360P视频一键转换HD
- 令代码停止_那些令人困惑的灯泡代码是什么意思?
- 夜神模拟器 android mprop 开启 ro.debuggable
- Window10 WiFi列表不显示解决方案
- Unity | Unity中UI框架的实现与使用
- Zuul网关的快速使用
- DELL(i7版本)麦克风杂音问题解决方案
- 小皮php的简单使用
热门文章
- 《Accurate eye center localisation by means of gradients》论文阅读
- 2016在电影院看过的电影
- 阿里云联合浙江大学举办首届数智服务创新挑战赛!
- 计算机的二三事——软件篇
- Deepin 系统安装NVIDIA
- “跨次元”检测模型hold住各种画风,真人赛博,在线Demo可玩
- 集成支付宝,跳转到支付宝后显示的不是支付页面
- 学习笔记(15):C++编程FFMpeg(QT5+OpenCV)实战--实时美颜直播推流-opencv播放rtsp海康摄像头和播放系统摄像头...
- 2017年ACM第八届山东省赛I题: Parity check(判断 第n项斐波那契数列奇偶性)
- 克劳士比语录(转载)