前言

  在查看系统内存监控的过程中,发现有几台机器的内存使用率一直很高,而且是呈现一个不太正常的高度,初始以为是 GC 不完全,也就是 JVM 内有大量对象不能回收,于是采用 Arthas 诊断查看一下机器的 JVM 使用情况。

  这是挑选的一台机器查看的 JVM 使用情况,上图截图部分为内存使用情况。主要看第一个 HEAP-MEMORY-USAGE。这是 jvm 中堆的使用情况,init 为堆初始化 3.0 GiB,used 为已经使用了 598 MiB,committed 为已提交内存,即空闲内存+使用内存,但是一直被 JVM 占用,并没有归还给操作系统,这就造成了我们在监控上看的时候这台机器的内存占用率一直高居不下的主要原因之一。

按照正常业务理解,Jvm 在触发 GC 后回收的空闲内存应该会释放一部分给操作系统,但实际上并没有这么做,下面通过一段代码测试验证 CMS 和 G1 的物理内存归还机制。

测试

  在测试中,需要至少两个线程,一个用来不断的创建大对象,一个用来手动触发系统 GC。同时采用 JProfiler 监控 JVM 堆内存变化。

测试代码如下:

 1 import java.util.ArrayList;
2 import java.util.List;
3 ​
4 /**
5 * @Author: Li.Jincheng
6 * @Date: 2022/1/24 18:36
7 * @Description:
8 */
9 public class JvmMemoryTest {
10 static volatile List<BigObject> list = new ArrayList<>();
11 ​
12 public static void main(String[] args) {
13 int count = 512;
14 Thread createObjectThread = new Thread(() -> {
15 try {
16 for (int i = 1; i <= 10; i++) {
17 System.out.println(String.format("第%s次生产%s大小的对象", i, count));
18 add(list, count);
19 //休眠10秒
20 Thread.sleep(10000);
21 }
22 } catch (InterruptedException e) {
23 e.printStackTrace();
24 }
25 });
26 ​
27 Thread clearThread = new Thread(() -> {
28 while (true) {
29 if (list.size() < count) {
30 continue;
31 }
32 //当List内存到达512M,就通知GC
33 System.out.println("清理list.... 回收jvm内存....");
34 list.clear();
35 //GC
36 System.gc();
37 //打印堆内存信息
38 printJvmMemoryInfo();
39 }
40 });
41 ​
42 // 启动线程
43 createObjectThread.start();
44 clearThread.start();
45 ​
46 try {
47 Thread.currentThread().join();
48 } catch (InterruptedException e) {
49 e.printStackTrace();
50 }
51 }
52 ​
53 /**
54 * 打印Jvm内存情况
55 */
56 public static void printJvmMemoryInfo() {
57 //虚拟机级内存情况查询
58 int byteToMb = 1024 * 1024;
59 Runtime runtime = Runtime.getRuntime();
60 long vmTotal = runtime.totalMemory() / byteToMb;
61 long vmFree = runtime.freeMemory() / byteToMb;
62 long vmMax = runtime.maxMemory() / byteToMb;
63 long vmUse = vmTotal - vmFree;
64 System.out.println("##############");
65 System.out.println("JVM内存已用的空间为:" + vmUse + " MB");
66 System.out.println("JVM内存的空闲空间为:" + vmFree + " MB");
67 System.out.println("JVM总内存空间为:" + vmTotal + " MB");
68 System.out.println("JVM总内存最大堆空间为:" + vmMax + " MB");
69 System.out.println("##############");
70 }
71 ​
72 /**
73 * 创建大对象列表
74 *
75 * @param list
76 * @param count
77 */
78 public static void add(List<BigObject> list, int count) {
79 for (int i = 0; i < count; i++) {
80 BigObject bigObject = new BigObject();
81 list.add(bigObject);
82 try {
83 Thread.sleep(50);
84 } catch (InterruptedException e) {
85 e.printStackTrace();
86 }
87 }
88 }
89 ​
90 public static class BigObject {
91 //生成5M的对象
92 private byte[] bytes = new byte[1024 * 1024 * 5];
93 }
94 }

CMS 垃圾回收器

配置:

-Xms128M -Xmx2048M -XX:+UseConcMarkSweepGC

结果:

  如上图所示,蓝色部分为实际使用内存,绿色部分为空闲内存,从图上可以看出在开始的时候即使触发了 GC 操作,JVM 回收了内存,但是并没有立即将空闲内存归还操作系统。在第三次 gc 后,可以明显看到 JVM 的空闲空间明显下降,这表明 JVM 已经归还了一部分内存,后面,随着 GC 次数增加慢慢的将内存归还。也就是说,CMS 垃圾回收器最终也会将申请的内存归还操作系统。

G1 垃圾回收器

配置:

-Xms128M -Xmx2048M -XX:+UseG1GC

结果:

  从上图可以看出,每触发一次 GC,JVM 的使用内存和空闲内存总和都降到了初始值 128M,也就是说在使用 G1 垃圾回收器时,每次 GC 都会将 JVM 新申请开辟的空间归还给操作系统。这也是一开始我们理解的垃圾回收机制以及预期结果。

总结

  CMS 垃圾回收器,在 JVM 申请内存后,会随着 GC 次数增加和频率足见拉长,从继续申请内存到慢慢归还给操作系统,知道如图所示出现一次全部归还后,每一次的 GC 都会将剩余空间归还操作系统;

  G1 垃圾回收器与之相反,每次 GC 后都会将内存全部归还操作系统,大大降低了机器的内存占用。

垃圾回收器比较:CMS 和 G1的更多相关文章

  1. [JVM 相关] Java 新型垃圾回收器(Garbage First,G1)

    回顾传统垃圾回收器 HotSpot 垃圾收集器实现 Serial Collector(串型收集器) 使用场景,大多数服务器是单核CPU. 适用收集场景:1. 新生代收集(Young Generatio ...

  2. 垃圾回收之CMS、G1、ZGC对比

    ZGC(The Z Garbage Collector)是JDK 11中推出的一款低延迟垃圾回收器,它的设计目标包括: 停顿时间不超过10ms: 停顿时间不会随着堆的大小,或者活跃对象的大小而增加: ...

  3. 探索ParNew和CMS垃圾回收器

    前言 上篇文章我们一起分析了JVM的垃圾回收机制,了解了新生代的内存模型,老年代的空间分配担保原则,并简单的介绍了几种垃圾回收器.详细内容小伙伴们可以去看一下我的上篇文章:秒懂JVM的垃圾回收机制. ...

  4. G1垃圾回收器在并发场景调优

    一.序言 目前企业级主流使用的Java版本是8,垃圾回收器支持手动修改为G1,G1垃圾回收器是Java 11的默认设置,因此G1垃圾回收器可以用很长时间,现阶段垃圾回收器优化意味着针对G1垃圾回收器优 ...

  5. Java GC系列(3):垃圾回收器种类

    本文由 ImportNew - 好好先生 翻译自 javapapers. 目录 垃圾回收介绍 垃圾回收是如何工作的? 垃圾回收的类别 垃圾回收监视和分析 在这篇教程中我们将学习几种现有的垃圾回收器.在 ...

  6. [译]Java垃圾回收器的类型

    说明:这篇文章来翻译来自于Javapapers 的Types of Java Garbage Collectors 在这部分的教程中我们将讲到可使用的四种不同类型的Java垃圾回收器.垃圾回收是Jav ...

  7. Hotspot JVM垃圾回收器

    前两篇<JVM入门——运行时数据区><JVM常见垃圾回收算法>所提到的实际上JVM规范以及常用的垃圾回收算法,具体的JVM实现实际上不止一种,有JRockit.J9等待,当然最 ...

  8. 一篇文章让你了解GC垃圾回收器

    简单了解GC垃圾回收器 了解GC之前我们首先要了解GC是要做什么的?顾名思义回收垃圾,什么是垃圾呢? GC回收的垃圾主要指的是回收堆内存中的垃圾对象. 从根对象出发,所有被引用的对象,都是存活对象 其 ...

  9. JVM 专题二十:垃圾回收(四)垃圾回收器 (一)

    1. GC分类与性能指标 垃圾收集器没有在规范中进行过多的规定,可以由不同的厂商.不同版本的JVM来实现.由于JDK的版本处于高速迭代过程中,因此Java发展至今已经产生了众多的GC版本.从不同角度分 ...

  10. 如何选择JVM垃圾回收器?

    明确垃圾回收器组合 -XX:+UseSerialGC 年轻代和老年代都用串行收集器 -XX:+UseParNewGC 年轻代使用ParNew,老年代使用 Serial Old -XX:+UsePara ...

随机推荐

  1. Vue cli之传递数据

    1.父组件的数据传递给子组件 // 父组件 <Menu title="来自Home的数据" :clickNum="num"></Menu> ...

  2. yapi 自动化安装遇到的问题

    yapi版本: 1.10.2 yapi server 启动之后 填写好配置,点击开始部署,发现控制台打印如下日志 (node:19879) Warning: Accessing non-existen ...

  3. Android 12(S) MultiMedia Learning(六)NuPlayer Decoder

    接下来将会从4个角度来记录NuPlayerDecoder部分 相关代码路径: http://aospxref.com/android-12.0.0_r3/xref/frameworks/av/medi ...

  4. VALL-EX下载介绍:只需3秒录音,即可克隆你的声音

    VALL-EX是一个强大和创新的多语言文本转语音模型,支持对中文.英文和日语的语音进行合成和克隆,使用者只需上传一段3-10秒的录音,就可以生成高质量的目标音频,同时保留了说话人的声音.情感和声学环境 ...

  5. Windows文件管理优化-实用电脑软件(一)

    RX文件管理器 (稀奇古怪的小软件,我推荐,你点赞!) 日后更新涉及:电脑.维护.清理.小工具.手机.APP.IOS.从WEB.到到UI.从开发,设计:诚意寻找伙伴(文编类.技术类.思想类)共编,共进 ...

  6. LeetCode 692. Top K Frequent Words 前K个高频单词 (Java)

    题目: Given a non-empty list of words, return the k most frequent elements. Your answer should be sort ...

  7. 使用极限网关助力 ES 集群无缝升级、迁移上/下云

    在工作中大家可能会遇到以下这些场景: 自建 ES 集群需要平滑迁移到 XX 云: 从 XX 云将 ES 集群迁移到自建机房: ES 集群进行跨版本升级,同时保留回退能力: 这些场景往往都还有个共同的需 ...

  8. OpenSSL生成加密证书.cer和.pfx

    linux自带openssl,所以在linux平台操作. 先输入: openssl 再输入: genrsa -out private-rsa.key 1024 生成私钥,无密码保护的私钥. 输入: r ...

  9. CentOS 7- 配置阿里镜像源

    1.备份CentOS 7系统自带yum源配置文件/etc/yum.repos.d/CentOS-Base.repo命令: mv /etc/yum.repos.d/CentOS-Base.repo /e ...

  10. 高并发缓存中间件Redis

    https://tech.meituan.com/2020/07/01/kv-squirrel-cellar.html 美团万亿级 KV 存储架构与实践 阿里云 redis文档 https://hel ...