我终于看懂了G1垃圾回收!原来JVM是这样打扫内存的

85 阅读4分钟

📚 文章导航

一、为什么需要G1?—— 从一次"打扫房间"的崩溃说起

你有没有过这样的经历:

  • 房间乱到不行,决定彻底打扫(就像程序进行垃圾回收)
  • 要么花一整天一次性搞定(结果当天什么正事都干不了)
  • 要么每天收拾一点(结果角落里的垃圾越积越多)

💡 程序的烦恼:Java程序也有同样的问题!垃圾回收时要么卡顿太久,要么内存碎片严重,直到G1出现才解决了这个两难问题。

二、G1最牛的想法:把内存切成"豆腐块"

图:G1内存Region划分示意图

以前的垃圾回收器把内存分成两大块:

  • 新东西放"新区域"(新生代)
  • 旧东西放"旧区域"(老年代)

就像家里只有两个房间,打扫时要么全打扫,要么只扫一个,太不方便了!

G1的创新:把内存切成很多小"豆腐块"(叫Region),每个豆腐块大小差不多(1MB到32MB)。每个豆腐块可以贴不同标签:

🍓 草莓味🍫 巧克力味🎂 蛋糕味🎁 礼物盒
放刚创建的新对象(Eden区)放活了一段时间的对象(Survivor区)放住了很久的老对象(Old区)专门放大对象(Humongous区)

✨ 这样做的好处:想打扫哪里就打扫哪里,不用整个房间都停工!

三、G1工作起来像什么?—— 四步打扫法

想象G1是个聪明的清洁工,它打扫房间分四步,尽量不打扰你工作:

🔍 第1步:快速看一眼(初始标记)

  • 动作:快速扫一眼,标记那些一眼就能看到的重要东西(被直接引用的对象)
  • 耗时:超短!就像你进门时快速看一眼沙发上有没有客人
  • 影响:程序会暂停一下,但几乎感觉不到

🔄 第2步:边看电视边打扫(并发标记)

  • 动作:你继续用电脑(程序正常运行),清洁工在后台慢慢检查每个东西是不是还有用
  • 耗时:比较长,但你完全感觉不到
  • 类比:就像妈妈边看电视边织毛衣,两不耽误

✅ 第3步:最后检查(最终标记)

  • 动作:再快速暂停一下,处理刚才打扫时新产生的垃圾
  • 耗时:很短,就像你出门前再检查一遍有没有忘带钥匙

🚀 第4步:先扔最臭的垃圾(筛选回收)

  • 动作:清洁工拿出小本本,上面记着每个豆腐块的垃圾量:"3号房80%是垃圾,5号房60%..."
  • 策略:优先打扫垃圾最多的房间(所以叫Garbage-First)
  • 时间控制:你可以告诉它"最多只能打扫50毫秒",它就会按这个时间选要打扫的房间
// 设置G1最大暂停时间为50ms
-XX:MaxGCPauseMillis=50

四、G1为什么这么受欢迎?

🎯 核心优势:G1就像一位会看表的清洁工,既不会让你等太久,又能把房间收拾干净!

1. ⏱️ 不会让你等太久

你可以设置每次打扫最多停多久(比如50ms),G1会尽量遵守承诺,就像外卖小哥说"最多晚到10分钟"

2. 🧹 房间永远整齐

传统清洁工只扔垃圾不整理,时间久了东西东倒西歪(内存碎片)。G1每次都会把东西摆整齐

3. 🏠 大房子也不怕

内存越大(堆越大),G1优势越明显。就像打扫100平米的房子,分区域打扫比一次性打扫高效多了


五、给新手的3个小建议

⚠️ 注意:这些建议能帮你避开90%的G1使用坑!

    内存别太小:G1在小内存上发挥不出优势,至少给4GB以上(就像小房间不需要专业清洁工)

    别太苛刻:别要求每次打扫时间太短(比如10ms以下),清洁工也需要合理时间

    少买超大件:少创建特别大的对象,就像别买太大的家具,不好搬也不好打扫

六、一句话总结

G1就像那个会看表的清洁工

  • 把你家(内存)分成很多小房间
  • 每次只打扫最脏的几个房间
  • 你说了算每次最多打扫多久
  • 打扫完还帮你把东西摆整齐