深入拆解Java虚拟机
郑雨迪
Oracle 高级研究员,计算机博士
立即订阅
28002 人已学习
课程目录
已完结 39 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 为什么我们要学习Java虚拟机?
免费
模块一:Java虚拟机基本原理 (12讲)
01 | Java代码是怎么运行的?
02 | Java的基本类型
03 | Java虚拟机是如何加载Java类的?
04 | JVM是如何执行方法调用的?(上)
05 | JVM是如何执行方法调用的?(下)
06 | JVM是如何处理异常的?
07 | JVM是如何实现反射的?
08 | JVM是怎么实现invokedynamic的?(上)
09 | JVM是怎么实现invokedynamic的?(下)
10 | Java对象的内存布局
11 | 垃圾回收(上)
12 | 垃圾回收(下)
模块二:高效编译 (12讲)
【工具篇】 常用工具介绍
13 | Java内存模型
14 | Java虚拟机是怎么实现synchronized的?
15 | Java语法糖与Java编译器
16 | 即时编译(上)
17 | 即时编译(下)
18 | 即时编译器的中间表达形式
19 | Java字节码(基础篇)
20 | 方法内联(上)
21 | 方法内联(下)
22 | HotSpot虚拟机的intrinsic
23 | 逃逸分析
模块三:代码优化 (10讲)
24 | 字段访问相关优化
25 | 循环优化
26 | 向量化
27 | 注解处理器
28 | 基准测试框架JMH(上)
29 | 基准测试框架JMH(下)
30 | Java虚拟机的监控及诊断工具(命令行篇)
31 | Java虚拟机的监控及诊断工具(GUI篇)
32 | JNI的运行机制
33 | Java Agent与字节码注入
模块四:黑科技 (3讲)
34 | Graal:用Java编译Java
35 | Truffle:语言实现框架
36 | SubstrateVM:AOT编译框架
尾声 (1讲)
尾声 | 道阻且长,努力加餐
深入拆解Java虚拟机
登录|注册

12 | 垃圾回收(下)

郑雨迪 2018-08-17
在读博士的时候,我曾经写过一个统计 Java 对象生命周期的动态分析,并且用它来跑了一些基准测试。
其中一些程序的结果,恰好验证了许多研究人员的假设,即大部分的 Java 对象只存活一小段时间,而存活下来的小部分 Java 对象则会存活很长一段时间。
(pmd 中 Java 对象生命周期的直方图,红色的表示被逃逸分析优化掉的对象)
之所以要提到这个假设,是因为它造就了 Java 虚拟机的分代回收思想。简单来说,就是将堆空间划分为两代,分别叫做新生代和老年代。新生代用来存储新建的对象。当对象存活时间够长时,则将其移动到老年代。
Java 虚拟机可以给不同代使用不同的回收算法。对于新生代,我们猜测大部分的 Java 对象只存活一小段时间,那么便可以频繁地采用耗时较短的垃圾回收算法,让大部分的垃圾都能够在新生代被回收掉。
对于老年代,我们猜测大部分的垃圾已经在新生代中被回收了,而在老年代中的对象有大概率会继续存活。当真正触发针对老年代的回收时,则代表这个假设出错了,或者堆的空间已经耗尽了。
这时候,Java 虚拟机往往需要做一次全堆扫描,耗时也将不计成本。(当然,现代的垃圾回收器都在并发收集的道路上发展,来避免这种全堆扫描的情况。)
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《深入拆解Java虚拟机》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(57)

  • godtrue 置顶
    写的非常棒,又帮我看到了一个小黑盒中的天地。
    小结:
    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
    1
    24
  • 公子_小白
    老师您好
    请问JVM分代收集新生代对象进入老年代,年龄为什么是15而不是其他的?
    谢谢

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

    2018-08-23
    13
  •  素丶  
    可以配合R大的文章
    http://rednaxelafx.iteye.com/blog/1042471
    http://rednaxelafx.iteye.com/blog/174865
    http://rednaxelafx.iteye.com/blog/1044951
    2018-11-20
    9
  • javaadu
    写得真好,搞清楚了之前没掌握透彻的概念:
    (1)TLAB是为了避免对象分配时对内存的竞争
    (2)卡表是为了处理minor gc时老年代对新生代的引用,为了避免整堆扫描而提出了卡表的概念

    提个问题:JVM中堆内存根系统的物理内存是如何对应的,这个可以从哪里看?

    作者回复: 根系统指的是?

    GC roots分布在HotSpot中的每个子系统里。可以在源码中搜oops_do

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

    作者回复: 多谢建议!

    2018-08-19
    1
    6
  • 愤怒的虾干
    1、当默认开启动态分配时,若ALIVE_OBJECT_SIZE小于33M几乎无FULL GC,大于则出现FULL GC。动态分配时Eden区大小不能小于Survivor区,即最少为新生代内存的1/3,单个Survivor区最大为新生代内存的1/3。故当ALIVE_OBJECT_SIZE大于33M时,Survivor区小于ALIVE_OBJECT_SIZE,导致Minor GC时需要复制到to区的数据大于to区容量,从而使得一部分数据提前晋升到老年区,多次提前晋升导致老年区无多余空间从而导致Full GC;当ALIVE_OBJECT_SIZE小于33M时,Survivor区容量总是近似的接近ALIVE_OBJECT_SIZE大小,使得每次Minor GC有效对象都可以复制到Survivor区,而晋升到老年代的对象大多是年龄达到次数要求,短期内不会挤满老年代空间,在有限时间内运行结束不会引发Full GC。
    2、当-XX:-UsePSAdaptiveSurvivorSizePolicy or -XX:SurvivorRatio=N关闭动态分配或指定Eden、Survivor比例时,只要ALIVE_OBJECT_SIZE小于Survivor容量,有限时间运行结束不会引发Full GC。反之会导致Full GC。
    @郑老师 是否是这样?
    2019-02-19
    3
  • 西兹兹
    老师好,请问java8开始之后的元数据区的回收是如何呢?这部分属于堆外内存吗?
    2019-01-19
    1
    3
  • 公子_小白
    多谢老师的回复
    2018-08-24
    2
  • 必然
    郑老师,你好!
    我想请教一个CMS收集器的问题,就是 CMS 在“重新标记”结束后,在进入“并发清除”时,这时候GC线程和应用线程同时在跑,如果在GC线程清理“垃圾”的时候,应用线程又将“垃圾”恢复为正常对象,怎么办呢?
    会出现这种情况吗?是虚拟机采用了某种方式杜绝了这种情况的发生么?还是有什么其它处理机制呢?
    谢谢!
    2019-09-10
    1
    1
  • river
    总结与实践中的代码ObjectOf64Bytes的64=6*8(6个long类型字段)+16(该对象的对象头大小,不是说压缩后是12字节么?)
    2019-09-09
    1
  • JackJin
    当发生 Minor GC 时,Eden 区和 from 指向的 Survivor 区中的存活对象会被复制到 to 指向的 Survivor 区中,然后交换 from 和 to 指针,以保证下一次 Minor GC 时,to 指向的 Survivor 区还是空的。
    这里的交换指针不理解,仅仅只是让to区每次都是空的?
    2019-08-27
    2
    1
  • Dd
    郑老师好,对于针对新生代的垃圾回收器共有三个:Serial,Parallel Scavenge 和 Parallel New。这三个采用的都是标记 - 复制算法,其中Serial GC,新生代使用标记复制算法这个说法,有官方出处吗?因为我先看的官方说明地址如下(JDK1.7)https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html,按照文档的定义
    Young Generation对应minor garbage collection,Old Generation对应major garbage collection

    然后在The Serial GC 收集器的说明中,有如下描述

    1.The serial collector is the default for client style machines in Java SE 5 and 6. With the serial collector, (both minor and major garbage collections are done serially) (using a single virtual CPU). In addition, it uses (a mark-compact) collection method

    对于Serial,算法到底是mark-compact 还是还是mark-copy?或者不同年代,还区分不同算法来进行垃圾收集?
    2019-06-30
    1
  • luffyke
    如果新生代晋升到老年代失败的时候如何处理?(可能原因:老年代内存不够,老年代碎片过多,晋升的新生代太大)
    2019-06-25
    1
  • 多巴胺老邮差
    郑老师您好 ! 一直想问问CMS收集器中并发标记这一个步骤的作用是什么 ? 为什么还要在初始标记的基础上多进行一次Roots Tracing呢? 希望您能有空能够回复下 thx
    2018-12-20
    1
  • 骑车吃火锅
    请问老师,java类是如何被卸载的?
    2018-12-11
    1
  • Leon Wong
    老师你好,写屏障不会判断引用是否指向新生代的对象,这里会不会提高MinorGC的成本呢?
    2018-09-12
    1
  • 永烁星光
    这里老年代对象存在对新生代对象的引用,那么这个指的引用是栈里存储的reference 吗? 还是对象内的引用

    作者回复: 对象内的引用

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

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

    2018-08-17
    1
  • 老白
    卡表用于堆内所有空间(新生代/老年代/永久代),还是只存在于老年代?
    2019-12-07
  • -W.LI-
    老师有两个问题请教。
    1.TLAB可是设置大小么?有时候一个方法中可能需要好几M的内存。吐过TLAB每次申请很少申请次数太多,对性能影响大么?还是hotSpot有优化,会递增每次申请的内存大小以减少申请次数?
    2.对像地址何时放入卡表中不太清楚。是老年代对象的局部变量被赋值就会把这个对象地址放入卡表么?方法中只有右移没写是否会判断当前对象是否是老年带对象。年轻带对象不会进入卡表的吧?
    3.遍历卡表时如果发现这个卡表内没有引用年轻代对象。会从卡表中删除么?
    2019-10-05
收起评论
57
返回
顶部