区块链技术博客
www.b2bchain.cn

08-聪明的jvm与对象的死亡求职学习资料

本文介绍了08-聪明的jvm与对象的死亡求职学习资料,有助于帮助完成毕业设计以及求职,是一篇很好的资料。

对技术面试,学习经验等有一些体会,在此分享。

  • 一 聪明的jvm
    • 1. 动态对象年龄判断
    • 2. 老年代空间分配担保机制
    • 3. 总结
  • 二 判断对象的死亡
    • 1. 引用计数算法
    • 2. 可达性分析算法

一 聪明的jvm

    上一节中我们知道了对象从诞生到死亡所经历的过程,和对象与垃圾收集机制的关系。那在进行MinorGC的时候,Survivor区的对象一定要等到年龄达到阈值了之后,才会进入到老年代吗?如果Survivor区都已经用尽了,但是对象的年龄远远没达到阈值,那又怎么办呢?如果MinorGC之后,将对象从eden区放入Survivor区的时候空间不够怎么办?如果老年代的可用空间已经远远小于新生代的所有对象空间,那么MinorGC之后能否安全将对象移入到老年代中呢?这种情况下是否直接进行FullGC呢?
    针对这些问题,jvm采用了一套灵活的机制来进行处理:对象动态年龄判断和老年代空间分配担保机制,以此降低系统的出错率和降低FullGC的频率,从而提高整体的性能。

1. 动态对象年龄判断

    为了能够更好的解决内存使用上的问题以及适应不同程序的内存状况,HotSpot虚拟机并不会要求对象的年龄必须达到阈值才能进入到老年代中,而是通过对象动态年龄判断的机制,来进行更合理的判断。
    该机制要求:当前存放对象的Survivor区域里(其中一块区域,放对象的那块s区),年龄1+年龄2+年龄n的多个年龄对象总和超过了为Survivor区域指定的阈值时(jvm默认为50%),此时就会把年龄n及以上的对象都放入老年代。
    有的同学可能看过周志明大佬的《深入理解Java虚拟机》一书,其中对动态对象年龄判断是这么描述的 “如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代” ,这和我的描述是有所出入的,如果按照这样的描述,想想会有什么样的问题?如果在survivor区中的对象,年龄为1的占25%,年龄为2的占30%,年龄为3的占30%,年龄为4的占15%,那不管怎么看,都不会有相同年龄的对象大小综合大于一半的情况,通过这种假设,我们发现有这样的问题,为了得到正确的结果,最好的办法就是查看hotspot的源码,如下在hotspot源码中,有这样一段代码

uint ageTable::compute_tenuring_threshold(size_t survivor_capacity) {    // 首先计算出阈值代表的空间大小   size_t desired_survivor_size = (size_t)((((double) survivor_capacity)*TargetSurvivorRatio)/100);     // 用于统计年龄代对象大小之和   size_t total = 0;   uint age = 1;   while (age < table_size) {       //sizes数组代表的是某个年龄代对象的总大小     total += sizes[age];       //当统计超过了阈值的时候,就中断统计     if (total > desired_survivor_size) break;     age++;   }   uint result = age < MaxTenuringThreshold ? age : MaxTenuringThreshold;

    从这段代码中可以看出,统计的对象是从年龄1逐步向上累加到年龄n,当总大小超过了阈值的时候,则将年龄代n及以上的对象放入到老年代中。在jvm中,可以通过参数(-XX:TargetSurvivorRatio)对这个阈值进行设置,改参数的默认值为50(代表50%)。对象动态年龄判断是在MinorGC之后进行的。
    通过对象动态年龄判断机制,就防止了上文出现的Survivor区会爆满的问题。

2. 老年代空间分配担保机制

    MinorGC必须要老年代有足够的空间才能够安全的进行对象迁移,在进行MinorGC之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象的总空间,如果不大,则要进行FullGC对老年代空间进行清理,假如我们MinorGC之后,eden区中的存活对象大于Survivor区的空间,这样就会有多余的对象只能放入到老年代中,放入老年代所需要的的空间是无法确定的,有可能大于老年代当前的可用空间,也有可能小于,因此这次MinorGC就是冒险的。而做FullGC的成本非常大的,并且对于jvm来说,不是每一次MinorGC移入到老年代的对象都会把老年代剩下的空间全部用尽。
    因此为了降低FullGC带来的性能影响,又要保证MinorGC的安全进行,因此就有了老年代空间分配担保机制:老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,则进行一次MinorGC。
    当然,如果通过老年代空间分配担保机制之后进行的MinorGC,存活的对象远高于历史平均值,导致了老年代也没有足够的空间来进行分配,就说明这次担保失败,那么需要再进行一次FullGC,这就导致了这次垃圾回收的过程被延长。
    在Java8中,已经没有了老年代空间分配担保机制的参数设置,默认就是使用这种机制的(若按照旧版本的jdk参数进行设置,会提示不能识别的参数),只要老年代的连续空间大于新生代对象总大小或者历次晋升的平均大小,就会进行MinorGC,否则将进行FullGC。

3. 总结

08-聪明的jvm与对象的死亡

二 判断对象的死亡

   前面了解了垃圾收集机制与运行时内存中对象的变化,那么对于jvm来说,如何去确定这些对象中哪些事存活的,哪些事死亡的呢?

1. 引用计数算法

    给对象中添加一个引用计数器,每当有一个地方引用它,计数器就加1;当引用失效,计数器就减1;任何时候计数器为0的对象就是不可能再被使用的。这个方法实现简单,效率高,但是目前主流的虚拟机中并没有选择这个算法来管理内存,其最主要的原因是它很难解决对象之间相互循环引用的问题。
    所谓对象之间的相互循环引用问题,就是对象 A 和 B 相互引用着对方之外,这两个对象不再被其它任何对象引用。但是他们因为互相引用对方,导致它们的引用计数器都不为0,于是引用计数算法无法通知GC回收器回收他们。
08-聪明的jvm与对象的死亡

    因此Java中并没有采用这种方式来判断对象的死活,而是采用了另一种算法:可达性分析算法。

2. 可达性分析算法

    Java虚拟机则是采用的可达性分析算法来判定对象是否存活的。该算法的基本思路就是通过一系列称为“GCRoots”的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为“引用链”(ReferenceChain),搜索到的对象都标记为非垃圾对象,如果某个对象到GCRoots间没有任何引用链相连时,则证明此对象是不可能再被使用的,即为垃圾对象。GC回收的就是这些未被标记为非垃圾的对象。

  • 一 聪明的jvm
    • 1. 动态对象年龄判断
    • 2. 老年代空间分配担保机制
    • 3. 总结
  • 二 判断对象的死亡
    • 1. 引用计数算法
    • 2. 可达性分析算法

一 聪明的jvm

    上一节中我们知道了对象从诞生到死亡所经历的过程,和对象与垃圾收集机制的关系。那在进行MinorGC的时候,Survivor区的对象一定要等到年龄达到阈值了之后,才会进入到老年代吗?如果Survivor区都已经用尽了,但是对象的年龄远远没达到阈值,那又怎么办呢?如果MinorGC之后,将对象从eden区放入Survivor区的时候空间不够怎么办?如果老年代的可用空间已经远远小于新生代的所有对象空间,那么MinorGC之后能否安全将对象移入到老年代中呢?这种情况下是否直接进行FullGC呢?
    针对这些问题,jvm采用了一套灵活的机制来进行处理:对象动态年龄判断和老年代空间分配担保机制,以此降低系统的出错率和降低FullGC的频率,从而提高整体的性能。

1. 动态对象年龄判断

    为了能够更好的解决内存使用上的问题以及适应不同程序的内存状况,HotSpot虚拟机并不会要求对象的年龄必须达到阈值才能进入到老年代中,而是通过对象动态年龄判断的机制,来进行更合理的判断。
    该机制要求:当前存放对象的Survivor区域里(其中一块区域,放对象的那块s区),年龄1+年龄2+年龄n的多个年龄对象总和超过了为Survivor区域指定的阈值时(jvm默认为50%),此时就会把年龄n及以上的对象都放入老年代。
    有的同学可能看过周志明大佬的《深入理解Java虚拟机》一书,其中对动态对象年龄判断是这么描述的 “如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代” ,这和我的描述是有所出入的,如果按照这样的描述,想想会有什么样的问题?如果在survivor区中的对象,年龄为1的占25%,年龄为2的占30%,年龄为3的占30%,年龄为4的占15%,那不管怎么看,都不会有相同年龄的对象大小综合大于一半的情况,通过这种假设,我们发现有这样的问题,为了得到正确的结果,最好的办法就是查看hotspot的源码,如下在hotspot源码中,有这样一段代码

uint ageTable::compute_tenuring_threshold(size_t survivor_capacity) {    // 首先计算出阈值代表的空间大小   size_t desired_survivor_size = (size_t)((((double) survivor_capacity)*TargetSurvivorRatio)/100);     // 用于统计年龄代对象大小之和   size_t total = 0;   uint age = 1;   while (age < table_size) {       //sizes数组代表的是某个年龄代对象的总大小     total += sizes[age];       //当统计超过了阈值的时候,就中断统计     if (total > desired_survivor_size) break;     age++;   }   uint result = age < MaxTenuringThreshold ? age : MaxTenuringThreshold;

    从这段代码中可以看出,统计的对象是从年龄1逐步向上累加到年龄n,当总大小超过了阈值的时候,则将年龄代n及以上的对象放入到老年代中。在jvm中,可以通过参数(-XX:TargetSurvivorRatio)对这个阈值进行设置,改参数的默认值为50(代表50%)。对象动态年龄判断是在MinorGC之后进行的。
    通过对象动态年龄判断机制,就防止了上文出现的Survivor区会爆满的问题。

2. 老年代空间分配担保机制

    MinorGC必须要老年代有足够的空间才能够安全的进行对象迁移,在进行MinorGC之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象的总空间,如果不大,则要进行FullGC对老年代空间进行清理,假如我们MinorGC之后,eden区中的存活对象大于Survivor区的空间,这样就会有多余的对象只能放入到老年代中,放入老年代所需要的的空间是无法确定的,有可能大于老年代当前的可用空间,也有可能小于,因此这次MinorGC就是冒险的。而做FullGC的成本非常大的,并且对于jvm来说,不是每一次MinorGC移入到老年代的对象都会把老年代剩下的空间全部用尽。
    因此为了降低FullGC带来的性能影响,又要保证MinorGC的安全进行,因此就有了老年代空间分配担保机制:老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,则进行一次MinorGC。
    当然,如果通过老年代空间分配担保机制之后进行的MinorGC,存活的对象远高于历史平均值,导致了老年代也没有足够的空间来进行分配,就说明这次担保失败,那么需要再进行一次FullGC,这就导致了这次垃圾回收的过程被延长。
    在Java8中,已经没有了老年代空间分配担保机制的参数设置,默认就是使用这种机制的(若按照旧版本的jdk参数进行设置,会提示不能识别的参数),只要老年代的连续空间大于新生代对象总大小或者历次晋升的平均大小,就会进行MinorGC,否则将进行FullGC。

3. 总结

08-聪明的jvm与对象的死亡

二 判断对象的死亡

   前面了解了垃圾收集机制与运行时内存中对象的变化,那么对于jvm来说,如何去确定这些对象中哪些事存活的,哪些事死亡的呢?

1. 引用计数算法

    给对象中添加一个引用计数器,每当有一个地方引用它,计数器就加1;当引用失效,计数器就减1;任何时候计数器为0的对象就是不可能再被使用的。这个方法实现简单,效率高,但是目前主流的虚拟机中并没有选择这个算法来管理内存,其最主要的原因是它很难解决对象之间相互循环引用的问题。
    所谓对象之间的相互循环引用问题,就是对象 A 和 B 相互引用着对方之外,这两个对象不再被其它任何对象引用。但是他们因为互相引用对方,导致它们的引用计数器都不为0,于是引用计数算法无法通知GC回收器回收他们。
08-聪明的jvm与对象的死亡

    因此Java中并没有采用这种方式来判断对象的死活,而是采用了另一种算法:可达性分析算法。

2. 可达性分析算法

    Java虚拟机则是采用的可达性分析算法来判定对象是否存活的。该算法的基本思路就是通过一系列称为“GCRoots”的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为“引用链”(ReferenceChain),搜索到的对象都标记为非垃圾对象,如果某个对象到GCRoots间没有任何引用链相连时,则证明此对象是不可能再被使用的,即为垃圾对象。GC回收的就是这些未被标记为非垃圾的对象。

  • 一 聪明的jvm
    • 1. 动态对象年龄判断
    • 2. 老年代空间分配担保机制
    • 3. 总结
  • 二 判断对象的死亡
    • 1. 引用计数算法
    • 2. 可达性分析算法

一 聪明的jvm

    上一节中我们知道了对象从诞生到死亡所经历的过程,和对象与垃圾收集机制的关系。那在进行MinorGC的时候,Survivor区的对象一定要等到年龄达到阈值了之后,才会进入到老年代吗?如果Survivor区都已经用尽了,但是对象的年龄远远没达到阈值,那又怎么办呢?如果MinorGC之后,将对象从eden区放入Survivor区的时候空间不够怎么办?如果老年代的可用空间已经远远小于新生代的所有对象空间,那么MinorGC之后能否安全将对象移入到老年代中呢?这种情况下是否直接进行FullGC呢?
    针对这些问题,jvm采用了一套灵活的机制来进行处理:对象动态年龄判断和老年代空间分配担保机制,以此降低系统的出错率和降低FullGC的频率,从而提高整体的性能。

1. 动态对象年龄判断

    为了能够更好的解决内存使用上的问题以及适应不同程序的内存状况,HotSpot虚拟机并不会要求对象的年龄必须达到阈值才能进入到老年代中,而是通过对象动态年龄判断的机制,来进行更合理的判断。
    该机制要求:当前存放对象的Survivor区域里(其中一块区域,放对象的那块s区),年龄1+年龄2+年龄n的多个年龄对象总和超过了为Survivor区域指定的阈值时(jvm默认为50%),此时就会把年龄n及以上的对象都放入老年代。
    有的同学可能看过周志明大佬的《深入理解Java虚拟机》一书,其中对动态对象年龄判断是这么描述的 “如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代” ,这和我的描述是有所出入的,如果按照这样的描述,想想会有什么样的问题?如果在survivor区中的对象,年龄为1的占25%,年龄为2的占30%,年龄为3的占30%,年龄为4的占15%,那不管怎么看,都不会有相同年龄的对象大小综合大于一半的情况,通过这种假设,我们发现有这样的问题,为了得到正确的结果,最好的办法就是查看hotspot的源码,如下在hotspot源码中,有这样一段代码

uint ageTable::compute_tenuring_threshold(size_t survivor_capacity) {    // 首先计算出阈值代表的空间大小   size_t desired_survivor_size = (size_t)((((double) survivor_capacity)*TargetSurvivorRatio)/100);     // 用于统计年龄代对象大小之和   size_t total = 0;   uint age = 1;   while (age < table_size) {       //sizes数组代表的是某个年龄代对象的总大小     total += sizes[age];       //当统计超过了阈值的时候,就中断统计     if (total > desired_survivor_size) break;     age++;   }   uint result = age < MaxTenuringThreshold ? age : MaxTenuringThreshold;

    从这段代码中可以看出,统计的对象是从年龄1逐步向上累加到年龄n,当总大小超过了阈值的时候,则将年龄代n及以上的对象放入到老年代中。在jvm中,可以通过参数(-XX:TargetSurvivorRatio)对这个阈值进行设置,改参数的默认值为50(代表50%)。对象动态年龄判断是在MinorGC之后进行的。
    通过对象动态年龄判断机制,就防止了上文出现的Survivor区会爆满的问题。

2. 老年代空间分配担保机制

    MinorGC必须要老年代有足够的空间才能够安全的进行对象迁移,在进行MinorGC之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象的总空间,如果不大,则要进行FullGC对老年代空间进行清理,假如我们MinorGC之后,eden区中的存活对象大于Survivor区的空间,这样就会有多余的对象只能放入到老年代中,放入老年代所需要的的空间是无法确定的,有可能大于老年代当前的可用空间,也有可能小于,因此这次MinorGC就是冒险的。而做FullGC的成本非常大的,并且对于jvm来说,不是每一次MinorGC移入到老年代的对象都会把老年代剩下的空间全部用尽。
    因此为了降低FullGC带来的性能影响,又要保证MinorGC的安全进行,因此就有了老年代空间分配担保机制:老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,则进行一次MinorGC。
    当然,如果通过老年代空间分配担保机制之后进行的MinorGC,存活的对象远高于历史平均值,导致了老年代也没有足够的空间来进行分配,就说明这次担保失败,那么需要再进行一次FullGC,这就导致了这次垃圾回收的过程被延长。
    在Java8中,已经没有了老年代空间分配担保机制的参数设置,默认就是使用这种机制的(若按照旧版本的jdk参数进行设置,会提示不能识别的参数),只要老年代的连续空间大于新生代对象总大小或者历次晋升的平均大小,就会进行MinorGC,否则将进行FullGC。

3. 总结

08-聪明的jvm与对象的死亡

二 判断对象的死亡

   前面了解了垃圾收集机制与运行时内存中对象的变化,那么对于jvm来说,如何去确定这些对象中哪些事存活的,哪些事死亡的呢?

1. 引用计数算法

    给对象中添加一个引用计数器,每当有一个地方引用它,计数器就加1;当引用失效,计数器就减1;任何时候计数器为0的对象就是不可能再被使用的。这个方法实现简单,效率高,但是目前主流的虚拟机中并没有选择这个算法来管理内存,其最主要的原因是它很难解决对象之间相互循环引用的问题。
    所谓对象之间的相互循环引用问题,就是对象 A 和 B 相互引用着对方之外,这两个对象不再被其它任何对象引用。但是他们因为互相引用对方,导致它们的引用计数器都不为0,于是引用计数算法无法通知GC回收器回收他们。
08-聪明的jvm与对象的死亡

    因此Java中并没有采用这种方式来判断对象的死活,而是采用了另一种算法:可达性分析算法。

2. 可达性分析算法

    Java虚拟机则是采用的可达性分析算法来判定对象是否存活的。该算法的基本思路就是通过一系列称为“GCRoots”的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为“引用链”(ReferenceChain),搜索到的对象都标记为非垃圾对象,如果某个对象到GCRoots间没有任何引用链相连时,则证明此对象是不可能再被使用的,即为垃圾对象。GC回收的就是这些未被标记为非垃圾的对象。

部分转自互联网,侵权删除联系

赞(0) 打赏
部分文章转自网络,侵权联系删除b2bchain区块链学习技术社区 » 08-聪明的jvm与对象的死亡求职学习资料
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

b2b链

联系我们联系我们