深入拆解 Java 虚拟机
郑雨迪
Oracle 高级研究员,计算机博士
87446 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 40 讲
模块四:黑科技 (3讲)
深入拆解 Java 虚拟机
15
15
1.0x
00:00/00:00
登录|注册

12 | 垃圾回收(下)

to
from
Survivor区
Eden区
ZGC
G1
CMS
Parallel Old
Serial Old
Parallel New
Parallel Scavenge
Serial
写屏障
脏卡
存活对象晋升至老年代
标记-复制算法
老年代
新生代
Java对象生命周期对垃圾回收的影响
针对老年代
针对新生代
卡表技术
Minor GC
分代回收思想
实践
垃圾回收器
垃圾回收算法
JVM垃圾回收

该思维导图由 AI 生成,仅供参考

在读博士的时候,我曾经写过一个统计 Java 对象生命周期的动态分析,并且用它来跑了一些基准测试。
其中一些程序的结果,恰好验证了许多研究人员的假设,即大部分的 Java 对象只存活一小段时间,而存活下来的小部分 Java 对象则会存活很长一段时间。
(pmd 中 Java 对象生命周期的直方图,红色的表示被逃逸分析优化掉的对象)
之所以要提到这个假设,是因为它造就了 Java 虚拟机的分代回收思想。简单来说,就是将堆空间划分为两代,分别叫做新生代和老年代。新生代用来存储新建的对象。当对象存活时间够长时,则将其移动到老年代。
Java 虚拟机可以给不同代使用不同的回收算法。对于新生代,我们猜测大部分的 Java 对象只存活一小段时间,那么便可以频繁地采用耗时较短的垃圾回收算法,让大部分的垃圾都能够在新生代被回收掉。
对于老年代,我们猜测大部分的垃圾已经在新生代中被回收了,而在老年代中的对象有大概率会继续存活。当真正触发针对老年代的回收时,则代表这个假设出错了,或者堆的空间已经耗尽了。
这时候,Java 虚拟机往往需要做一次全堆扫描,耗时也将不计成本。(当然,现代的垃圾回收器都在并发收集的道路上发展,来避免这种全堆扫描的情况。)
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文介绍了Java虚拟机中的垃圾回收技术,重点讨论了针对新生代的Minor GC和卡表技术。文章首先介绍了Java对象生命周期的动态分析,引出了Java虚拟机的分代回收思想,并详细解释了堆划分、内存分配过程和Minor GC的过程。随后,重点介绍了卡表技术,解释了其在Minor GC中的作用和实现原理。文章还提到了写屏障和虚共享问题,以及Java虚拟机中的垃圾回收器。总结部分对Java虚拟机中垃圾回收器进行了简要介绍,并指出了G1和ZGC的特点。整体来看,本文深入浅出地介绍了JVM垃圾回收的相关知识,对于想深入了解Java虚拟机内存管理的读者具有很高的参考价值。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《深入拆解 Java 虚拟机》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(86)

  • 最新
  • 精选
  • 置顶
    写的非常棒,又帮我看到了一个小黑盒中的天地。 小结: 1:二八法则-适用于许多的领域,对象在JVM对内存空间的生命周期也同样符合 2:为了更好的JVM性能以及充分利用对象生命周期的二八法则,JVM的作者将JVM的对内存空间进行了分代的处理 3:堆内存空间=年轻代+老年代 年轻代=Eden+from+to 年轻代用于分配新生的对象 Eden-通常用于存储新创建的对象,对内存空间是共享的,所以,直接在这里面划分空间需要进行同步 from-当Eden区的空间耗尽时,JVM便会出发一次Minor GC 来收集新生代的垃圾,会把存活下来的对象放入Survivor区,也就是from区 注意,from和to是变动的 to-指向的Survivor区是空的,用于当发生Minor GC 时,存储Eden和from区中的存活对象,然后再交换from和to指针,以保证下一次Minor GC 时to指向的Survivor区还是空的。 老年代-用于存储存活时间更久的对象,比如:15次Minor GC 还存活的对象就放入老年代中 4:堆内存分代后,会根据他们的不同特点来区别对待,进行垃圾回收的时候会使用不同的垃圾回收方式,针对新生代的垃圾回收器有如下三个:Serial、Parallel Scavenge、Parallel New,他们采用的都是标记-复制的垃圾回收算法。 针对老年代的垃圾回收器有如下三个:Serial Old 、Parallel Old 、CMS,他们使用的都是标记-压缩的垃圾回收算法。 5:TLAB(Thread Local Allocation Buffer)-这个技术是用于解决多线程竞争堆内存分配问题的,核心原理是对分配一些连续的内存空间 6:卡表-这个技术是用于解决减少老年代的全堆空间扫描

    作者回复: 好长的总结,赞一个

    2018-08-21
    4
    67
  • 公子_小白
    老师您好 请问JVM分代收集新生代对象进入老年代,年龄为什么是15而不是其他的? 谢谢

    作者回复: HotSpot会在对象头中的标记字段里记录年龄,分配到的空间只有4位,最多只能记录到15

    2018-08-23
    5
    63
  • javaadu
    写得真好,搞清楚了之前没掌握透彻的概念: (1)TLAB是为了避免对象分配时对内存的竞争 (2)卡表是为了处理minor gc时老年代对新生代的引用,为了避免整堆扫描而提出了卡表的概念 提个问题:JVM中堆内存根系统的物理内存是如何对应的,这个可以从哪里看?

    作者回复: 根系统指的是? GC roots分布在HotSpot中的每个子系统里。可以在源码中搜oops_do

    2018-08-17
    3
    16
  • Shine
    通过GC_roots探索存活对象的时候,有的对象在新生代,有的对象在老年代。如果当前对象处在老年代而且在赃卡中,需要继续深度遍历对象。若不在赃卡中,就没必要进一步深度遍历下去了。不知道我的理解对不?

    作者回复: 对的。卡表就是为了避免探索整个老年代,而只将脏表中的对象作为GC roots。

    2018-08-17
    14
  • herome
    老师 建议是能画点图吗 😂 每一篇都是文字。 相信画了图 不仅能让文章通俗易懂,也能让老师的文笔更好。

    作者回复: 多谢建议!

    2018-08-19
    2
    13
  • Ben
    老师,有个疑问想请教一下: 如果Eden区和from指向的Survivor区存活对象大于to指向的Survivor区的大小,那么JVM会如何处理?

    作者回复: 会按对象年龄晋升最老的那些至老年代。极端情况,假设survivor 区大小为0,那就是直接把eden区的存活对象晋升过去了

    2019-12-23
    2
    7
  • Jimbol
    看某些资料介绍说tlab是将对象创建在栈内存上,并且与逃逸分析一起用,这样在释放栈内存时就直接将这部分内存一起释放了,降低了gc成本。您讲解的说tlab是提前在堆内存中分配空间,这样没有降低gc成本呀!

    作者回复: HotSpot不支持在栈上新建对象。 C2里的逃逸分析是静态分析,和TLAB没什么关系。它和标量替换一起使用,能够完全不分配对象,仅在寄存器中维护这个对象的字段。

    2018-08-18
    2
    7
  • abs
    新声代回收的时候会把老年代的所有对象当做gcroot吗

    作者回复: 原本是的。但使用了脏卡技术之后,JVM只需要把脏卡中的对象当成GC roots

    2018-08-29
    6
  • Alex Rao
    TLAB 和 工作内存是什么关系?

    作者回复: 如果你说的是JMM中的工作内存,那是JMM抽象出来的一个概念,在现实的体系架构中可以映射为CPU中的缓存,属于硬件技术。 TLAB是用来优化多线程分配的,属于软件技术。两者没啥关系。

    2018-08-19
    6
  • 郭俊杰
    老师,你好,一直想问个问题,多线程访问共享变量会存在线程安全问题,那么方法内部通过new的局部变量是不是也存在安全问题呢?希望老师帮忙回答下感谢

    作者回复: 如果你把new后的对象发布出去,就是放到其他共享变量中,也会存在线程安全问题。如果new后只是在方法中使用该对象而不作为参数传出去(以它为调用者的实例方法也不会传出去),那么没有问题。

    2018-08-18
    5
收起评论
显示
设置
留言
86
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部