上一篇丹丹学妹已经给我讲了七种经典的GC器,那到底怎么样的GC器才能称的上"完美"?
asda这就涉及到了衡量GC器性能的三项最重要的指标:①、内存占用②、吞吐量 ③、低时延 ,有人会说那我们做出一个同时满足这三个指标的GC器不就好了嘛,可世间怎么可能事事都十全十美,能满足其中的两项,就算不错的GC器了。
asda而随着计算机硬件的发展,其内存大了,性能好了,我们对内存的要求也放宽了限制(可以容忍GC器多占用一点内存),自然吞吐量也上去了(因为性能好了)。可是,内存大了那么时延必定相应增大了。

assadasdasdsadsadasdssadda我们通过一个图表示不同回收器的并发情况对比:

我们可以发现:
①、Serial,Parallel : 回收过程的所有步骤都要挂起用户线程,也就是STW,随着内存的增大,堆自然增大,那么停顿时间也会随之增长
②、CMS: 使用 增量更新技术,实现了标记阶段的并发,不会随着堆的增大停顿时间也增大。但是对于标记之后的回收处理,并不是很好:标记之后通过 “标记-清除” 算法虽然也可以做到并发,但是会产生空间碎片,早晚也要进行 Full GC
③、G1: 使用 原始快照技术,实现了标记阶段的并发,不会随着堆的增大停顿时间也增大。但是对于标记之后的回收处理,同样有缺陷:虽然可以按更小的粒度Region进行回收,从而降低了时延,但是随着堆的增大,时延也会增大
④、Shenandoah,ZGC: 几乎整个工作过程全部都是 并发的,只有初始标记、最终标记这些阶段有短暂的停顿,而这部分 停顿的时间基本上是固定的,与堆的容量、堆中对象的数量 没有正比例关系。不过这两款GC器目前都在 实验阶段(我没调查这两年是不是还处于这个阶段)。但是这两款都可以称为 低时延GC器。 (回收的改进)

assadasdasdsadsadassadda接下来我们分别介绍Shenandoah,ZGC收集器:


asadasaShenandoah收集器:

  • 地位:Shenandoah是一款只有 OpenJDK才会包含,而 OracleJDK里反而不存在的收集器,因为它不是由Oracle(包括以前的Sun)公司的虚拟机团队所领导开发的HotSpot垃圾收集器。
  • G1与Shenandoah的比较:
    ⒈相同点: Shenandoah和G1收集器都有着 相似的堆内存布局,在初始标记、并发标记等许多阶段的处理思路上都是一样的,并共享了一部分实现代码,比如它们都有在并发失败后作为“逃生门”的Full GC,都是使用基于Region的堆内存布局,同样有着用于存放大对象的HumongousRegion,默认的回收策略也同样是优先处理回收价值最大的Region等等
    ⒉不同点:
    asaa①、Shenandoah支持 并发的“复制-整理”
    asaa②、Shenandoah默认 不使用分代收集,也就是说不会有新生代,老生代;
    asaa③、Shenandoah摒弃了在G1中耗费大量内存和计算资源去维护的 记忆集,改用名为“ 连接矩阵”(Connection Matrix)的全局数据结构来 记录跨Region的引用关系降低了处理跨代指针时的记忆集维护消耗,也降低了伪共享问题的发生概率。
    【连接矩阵的使用】:连接矩阵可以理解为一个二阶矩阵,如果Region N有对象指向Region M,就在矩阵的N行M列处做标记,如图。

    asadasaShenandoah收集器的工作过程:
  • ①、初始标记(同G1一样): 标记 与GC Roots直接关联的对象,STW,但是与堆大小无关,只和GC Roots的数量有关;
  • ②、并发标记(同G1一样): 遍历对象图,标记出全部可达的对象,和用户线程并发;
  • ③、最终标记(同G1一样): 处理剩余的SATB扫描,并回收价值最高的Region,令其构成回收集,STW;
  • ④、并发清理: 清理整个区域内连一个对象都没有找到的Region;
  • ⑤、并发回收(与G1的核心差异): Shenandoah要把 回收集里面的存活对象先复制一份到其他未被使用的Region之中
    【注】: 怎么做到并发复制呢? 我们先看其其难点:在移动对象的同时,用户线程仍然可能不停对被移动的对象进行读写访问,移动对象是一次性的行为,但移动之后整个内存中所有指向该对象的引用都还是旧对象的地址,这是很难一瞬间全部改变过来的。针对这一困难,Shenandoah的处理方式:通过 读屏障和被称为 Brooks Pointers的转发指针来解决。并发回收阶段运行的时间长短取决于回收集的大小。
  • ⑥、初始引用更新: 把堆中所有指向旧对象的引用修正到复制后的新地址的这个操作称为引用更新;STW很短;
    【注】:引用更新的初始化阶段实际上并未做什么具体的处理,设立这个阶段只是为了建立一个线程集合点,确保所有并发回收阶段中进行的收集器线程都已完成分配给它们的对象移动任务而已;
  • ⑦、并发引用更新: 真正开始进行引用更新操作,这个阶段是与用户线程一起 并发的,它不再需要沿着对象图来搜索,只需要按照内存物理地址的顺序,线性地搜索出引用类型,把旧值改为新值即可。
  • ⑧、最终引用更新: 修正存在于GC Roots中的引用。这个阶段是Shenandoah的最后一次停顿,停顿时间只与GC Roots的数量相关;
  • ⑨、并发清理: 最后再调用一次并发清理过程来回收这些Region的内存空间,供以后新对象分配使用;
    【注】:
    Shenandoah收集器的工作过程大致可以划分为以上九个阶段(这是Shenandoah的早期版本划分),不过在Shenandoah2.0中进一步强化了“部分收集”的特性,初始标记之前还有Initial Partial、Concurrent Partial和Final Partial阶段,它们可以不太严谨地理解为对应于以前分代收集中的Minor GC的工作)

asadasaShenandoah收集器的支持并发整理的核心概念:

  • Brooks Pointer : “Brooks”是一个人的名字。1984提出了使用 转发指针(Forwarding Pointer,也常被称为 Indirection Pointer)来 实现对象移动与用户程序并发的一种解决方案
  • 在这之前,我们要实现类似的并发操作的大致思路如下:
    sdaasdsa①、通常是在被移动对象 原有的内存上设置保护陷阱
    sdaasdsa②、一旦用户程序访问到归属于旧对象的内存空间就会产生 自陷中段,进入预设好的 异常处理器中, 再由其中的代码逻辑把访问转发到 复制后的新对象上。
    sdaasds 【缺点】: 虽然确实能够实现对象移动与用户线程并发但是如果没有操作系统层面的直接支持,这种方案将导致用户态频繁切换到核心态,代价是非常大的,不能频繁使用。
  • 如今的新方案: 不需要用到内存保护陷阱,而是在原有对象布局结构的最前面统一增加一个新的引用字段 ,如图:

    【转发指针的性能分析】:
    sddsa⒈从结构上看,转发指针早期JVM使用的句柄定位比较像:两者都是一种 间接性的对象访问方式,差别是句柄通常会 统一存储在专门的句柄池中,而转发指针是 分散存放在每一个对象头前面
    sddsa⒉既然都是间接性的对象访问方式,那么就有特有的 缺点:每次对象访问会带来一次额外的转向开销,尽管这个开销已经被优化到只有一行汇编指令的程度。
    sddsa⒊虽然这是一比不小的开销,但是 对于内存保护陷阱方案已经有了很大的改善: 因为当我们复制对象时,也就是对象有了副本,我们仅仅需要修改一处指针的值,也就是旧对象上转发指针的引用位置,使其指向副本,也就是指向新的对象,这样便可将所有对该对象的访问转发到了新的副本上(这样只要旧对象的内存仍然存在,未被清理掉,虚拟机内存中所有通过旧引用地址访问的代码便仍然可用,都会被自动转发到新对象上继续工作)。

    【转发指针的相关操作所需要的一些注意事项】:
    sddsa⒈ 对转发指针的访问操作采取同步措施: 让收集器线程或者用户线程对转发指针的访问只有其中之一能够成功,另外一个必须等待,避免两者交替进行; 而我们可以通过 CAS 操作保证这种行为。
    sddsa⒉ 执行频率问题: 尽管 通过对象头上的Brooks Pointer来保证并发时原对象与复制对象的访问一致性,但是这个对象的访问其实是很复杂的:包括对象的 读取、写入,对象的比较,为对象哈希值计算,对象加锁等;而我们要覆盖全部对象访问操作,Shenandoah不得不同时设置 读、写屏障 去拦截。
    【读、写屏障的一些问题】:
    sddsa⒈ 写屏障: 在上一篇文章的介绍七种经典回收器中, 无论是为了维护卡表,还是用于实现并发标记,写屏障已被使用多次,累积了不少的处理任务了,这些写屏障有相当一部分在Shenandoah收集器中依然要被使用到。
    sddsa⒉ 读屏障: 为了实现 Brooks Pointer, Shenandoah在读、写屏障中都加入了额外的转发处理,不过代码里对象读取的出现频率要比对象写入的频率高出很多,读屏障数量自然也要比写屏障多得多,所以读屏障的使用必须更加谨慎,不允许任何的 重量级操作Shenandoah是我们所分析的所有回收机中第一款使用到读屏障的收集器,
    sddsa⒊ 读屏障的改进: JDK 13中将Shenandoah的内存屏障模型改进为 基于引用访问屏障 的实现,所谓 “引用访问屏障”是指内存屏障只拦截对象中数据类型为引用类型的读写操作,而不去管原生数据类型等其他非引用字段的读写,这能够省去大量对原生类型、对象比较、对象加锁等场景中设置内存屏障所带来的消耗。
    【Shenandoah收集器的强弱项】:
    sddsa⒈ 强项: (低延迟时间)建立量化的概念;
    sddsa⒉ 弱项: 高运行负担使得吞吐量下降;

【总结】: Shenandoah收集器中用到的新的技术:Brooks Pointer转发指针、读屏障(引用访问屏障)


asadasaZGC收集器:

  • ZGC 是一款在JDK 11中新加入的具有实验性质的 低延迟垃圾收集器,是由Oracle公司研发的;
  • 目标: ZGC和Shenandoah的目标是高度相似的,都希望在尽可能对吞吐量影响不太大的前提下,实现在任意堆内存大小下都可以把垃圾收集的停顿时间限制在十毫秒以内的低延迟
    【注】: Oracle公司开发的ZGC就更像是AzulSystem公司独步天下的 PGC 和 C4 收集器的同胞兄弟,早在2005年,运行在Azul VM上的PGC就已经 实现了标记和整理阶段都全程与用户线程并发运行的垃圾收集,而ZGC几乎所有的关键技术上,与PGC和C4都只存在术语称谓上的差别。
  • 主要特征: GC采用 基于Region的堆内存布局,(暂时)不设分代的,使用了 读屏障染色指针内存多重映射等技术来实现 可并发的标记-整理算法的,以低延迟为首要目标的一款垃圾收集器
    【Region的差异】: ZGC的Region(在一些官方资料中将它称为Page或者ZPage,本文为行文一致继续称为Region)具有动态性——动态创建和销毁,以及动态的区域容量大小。在x64硬件平台下,ZGC的 Region可以具有大、中、小三类容量:
    sdsdsa① 小型Region: 容量固定为2MB,用于放置对象≤256KB;
    sdsdsa② 中型Region: 容量固定为32MB,用于放置256KB≤对象<4MB的对象;
    sdsdsa③ 大型Region: 容量不固定,可以动态变化,但必须为2MB的整数倍,用于放置 4MB或以上的大对象。每个大型Region中只会存放一个大对象,这也预示着虽然名字叫作“大型 Region”,但它的实际容量完全有可能小于中型Region,最小容量可低至4MB。大型Region在ZGC的实现中是不会被重分配(重分配是ZGC的一种处理动作,用于复制对象的收集器阶段,稍后会介绍到) 的,因为复制一个大对象的代价非常高昂。

    asadasaZGC的核心问题:并发整理算法的实现。
  • 在上文我们分析过,Shenandoah使用 转发指针读屏障来实现并发整理;而ZGC更加巧妙,那就是 染色指针技术来替代转发指针
  • 在上文我们已经说过对象访问的相关操作,如果我们要在对象上存储一些额外的、只供收集器或者虚拟机本身使用的数据,通常会在对象头中增加额外的存储字段,如对象的哈希码、分代年龄、锁记录等就是这样存储的。 那我们考虑一个题:当这种在 对象头记录信息的方式,在有对象访问的情况下自然是很好的一种选择,但是如果这个对象被移动了呢?如果我们只是 单纯了解某些信息(比如对象是否移动过,比如对象是否被引用(三色标记),而不是访问对象呢?那么我们就要考虑别的记录信息的方式,比如通过指针或者与内存无关的地方得到这些信息。
  • 我们接下来看一下HotSpot虚拟机不同的收集器的不同的标记实现方式:
    sddsa⒈Serial收集器:把 标记直接记录在对象头上;
    sddsa⒉G1、Shenandoah:把 标记记录在与对象相互独立的数据结构上(一种相当于堆内存的1/64大小的,称为BitMap的结构来记录标记信息);
    sddsa3 ZGC:染色指针技术,是最直接的、最纯粹的,它直接把标记信息记在引用对象的指针上,这时,与其说可达性分析是遍历对象图来标记对象,还不如说是 遍历“引用图”来标记“引用”了
  • 染色体技术到底是什么?
    染色指针是一种直接将少量额外的信息存储在指针上的技术;
  • 指针怎么存储的呢?
    sddsa不同的操作系统采取不同的存储方式,大致都一样,只不过位数不一样。比如在64位系统中,理论上可以存储2^64的字节,不过实际上是做不到的,当然也用不到那么多内存(因为位数越长,在做地址转换的时候需要的页表级数越多,成本也更高),在AMD64架构中只支持到 52位(4PB)的地址总线和48位(256TB)的虚拟地址空 间,所以目前64位的硬件实际能够支持的最大内存只有 256TB。此外,操作系统一侧也还会施加自己的约束,所以所使用的位数就更小了。
    比如:
    sddsa64位的Linux则分别支持47位(128TB)的进程虚拟地址空间和46位(64TB)的物理地址空间
    sddsa64位的Windows系统只支持44位(16TB)的物理地址空间。
    sddsa尽管Linux下64位指针的高18位不能用来寻址,但剩余的46位指针所能支持的64TB内存在今天仍然能够充分满足大型服务器的需要。鉴于此,ZGC的染色指针技术继续盯上了这剩下的46位指针宽度,将其高4位提取出来存储四个标志信息通过这些标志位,虚拟机可以直接从指针中看到其引用对象的三色标记状态、是否进入了重分配集(即被移动过)、是否只能通过finalize()方法才能被访问到。当然,由于这些标志位进一步压缩了原本就只有46位的地址空间,也直接导致 ZGC能够管理的内存不可以超过4TB(2^42);
  • 染色指针的三大优势:
    sddsa⒈染色指针可以使得一旦某个Region的存活对象被移走之后,这个Region立即就能够被释放和重用,而不必等待整个堆中所有指向该Region的引用都被修正后才能清理。 这点相比起Shenandoah是一个颇大的优势,使得理论上只要还有一个空闲Region,ZGC就能完成收集,而Shenandoah需要等到引用更新阶段结束以后才能释放回收集中的Region,这意味着堆中几乎所有对象都存活的极端情况,需要1∶1复制对象到新Region的话,就必须要有一半的空闲Region来完成收集。
    sddsa⒉染色指针可以大幅减少在垃圾收集过程中内存屏障的使用数量设置内存屏障,尤其是写屏障的目的通常是为了记录对象引用的变动情况,如果将这些信息直接维护在指针中,显然就可以省去一些专门的记录操作。实际上,到目前为止ZGC都并未使用任何写屏障(这也说明ZGC对吞吐量影响很小),只使用了读屏障(一部分是染色指针的功劳,一部分是ZGC现在还不支持分代收集天然就没有跨代引用的问题
    sddsa[注]: 内存屏障(Memory Barrier)的目的是为了指令不因编译优化、CPU执行优化等原因而导致乱序执行,它也是可以细分为仅确保读操作顺序正确性和仅确保写操作顺序正确性的内存屏障的。
    sddsa3 染色指针可以作为一种可扩展的存储结构用来记录更多与对象标记、重定位过程相关的数据,以 便日后进一步提高性能。 现在Linux下的64位指针还有前18位并未使用,它们虽然不能用来寻址,却可以通过其他手段用于信息记录。如果开发了这18位,既可以腾出已用的4个标志位,将ZGC可支持的最大堆内存从4TB拓展到64TB,也可以利用其余位置再存储更多的标志,譬如存储一些追踪信息来让垃圾收集器在移动对象时能将低频次使用的对象移动到不常访问的内存区域。
  • 染色体指针技术带来的问题:Java虚拟机作为一个普普通通的进程, 这样随意重新定义内存中某些指针的其中几位,操作系统是否支持?处理器是否支持(多重映射技术)?
    sddsa无论中间过程如何,程序代码最终都要转换为 机器指令流 交付给处理器去执行,处理器可不会管指令流中的指针哪部分存的是标志位,哪部分才是真正的寻址地址,只会把整个指针都视作一个内存地址来对待。
    sddsa这个问题在 Solaris/SPARC平台上比较容易解决,因为SPARC硬件层面本身就支持虚拟地址掩码,设置之后其机器指令直接就可以忽略掉染色指针中的标志位。但在x86-64平台上并没有提供类似的黑科技,ZGC设计者就只能采取其他的补救措施了,这里的解决方案涉及 虚拟内存映射技术。
    sddsaLinux/x86-64平台上的ZGC使用了 多重映射(Multi-Mapping)将多个不同的虚拟内存地址映射到同一个物理内存地址上,这是一种多对一映射,意味着ZGC在虚拟内存中看到的地址空间要比实际的堆内存容量来得更大。把染色指针中的标志位看作是地址的分段符,那只要将这些不同的地址段都映射到同一个物理内存空间,经过多重映射转换后,就可以使用染色指针正常进行寻址了(不清楚的可以去看操作系统)。

    sddsa在某些场景下,多重映射技术确实可能会带来一些诸如复制大对象时会更容易这样的额外好处, 可从根源上讲,ZGC的多重映射只是它采用染色指针技术的伴生产物,并不是专门为了实现其他某种 特性需求而去做的。

asadasaZGC的运作过程

  • ZGC的运作过程大致可划分为以下四个大的阶段(都是并发过程):
  • ①、并发标记(Concurrent Mark):
    sddsa⒈初始标记(Mark Start): 先STW,并记录下 GC Roots直接引用的对象
    sddsa⒉并发标记(Concurrent Mark): 根据初始标记的结果,基于GC Roots可达性分析算法找出所有被引用的对象,在 G1、Shenandoah中使用 BitMap 的结构来记录三色标记信息ZGC使用 染色指针 做标记
    s dsa ⒊最终标记(Mark End): 先STW,然后修复一些在并发标记过程中垃圾状态出现变化的对象。
  • ②、并发预备重分配(Concurrent Prepare for Relocate): 这个阶段ZGC会根据特定的查询条件扫描一下所有的Region并得出本次收集过程中 需要清理哪些Region,将它们重新组成重 分配集(Relocation Set)用范围更大的扫描成本换取省去G1中记忆集的维护成本。
  • ③、并发重分配(Concurrent Relocate):
    sddsa⒈初始重分配(Relocate Start): 做一些并发重分配的初始化动作。
    sddsa⒉并发标记(Concurrent Mark): 这个阶段需要将并发预备重分配阶段计算出来的重分配集中的Region复制到新的Region并为每一个Region维护一个转发表(Forward Table),记录从旧对象到新对象的转向关系,从转发表ZGC就可以明确的知道哪些对象是否处于重分配集之中,在这个阶段时,如果有用户线程访问这个对象,这次访问将会被预置的内存屏障(读屏障)所截获,然后根据Region的转发表找出新的地址并访问,如果有更新在更新地址上的值,并使其指向新对象(这样子只有第一次访问时会变慢,后面的就可以不通过读屏障和转发表直接访问),ZGC将这种行为称为指针的“自愈”(Self-Healing)能力。
    sddsa【注】: 一旦一个Region中的对象全部复制完成,旧的Region就可以清理释放掉了,但是转发表不能立即释放,因为可能还有访问在使用这个转发表,因为对象的旧地址转新地址是对象在被引用之后才会进行的操作。
  • ④、并发重映射(Concurrent Remap): 重映射其实就是将旧的地址转换为新的地址,由于ZGC中对象引用存在“自愈”功能,所以这个阶段其实不做也是可以的,ZGC很巧妙的将这一阶段合并到了下一次的并发标记阶段,反正他们都是要遍历所有对象的,这样也就减少了一次遍历对象的开销,一个Region的所有对象都被修改后,那么这个Region对应的转发表就会被销毁掉。

asadasa相比G1、Shenandoah,ZGC的优劣

  • 相比 G1、Shenandoah等先进的垃圾收集器,ZGC在实现细节上做了一些不同的权衡选择:
    sda⒈ 比如,G1 需要通过 写屏障来维护 记忆集,才能处理 跨代指针,得以 实现Region的增量回收。而记忆集要占用大量的内存空间,写屏障也对正常程序运行造成额外负担,这些都是权衡选择的代价。
    sdd⒉ ZGC就 完全没有使用记忆集,它甚至 连分代都没有,连像CMS中那样只记录新生代和老年代间引用的 卡表也不需要因而完全没有用到写屏障,所以给用户线程带来的运行负担也要小得多。
  • 可是,必定要有优有劣才会称作权衡,ZGC的这种选择也限制了它能承受的对象分配速率不会太高
    【可以想象以下场景来理解 ZGC的这个劣势】: ZGC准备要对一个很大的堆做一次完整的并发收集,假设其全过程要持续十分钟以上(切勿混淆并发时间与停顿时间,ZGC立的Flag是停顿时间不超过十毫秒),在这段时间里面,由于应用的对象分配速率很高,将创造大量的新对象,这些新对象很难进入当次收集的标记范围,通常就只能全部当作存活对象来看待——尽管其中绝大部分对象都是朝生夕灭的,这就产生了大量的 浮动垃圾。如果这种高速分配持续维持的话,每一次完整的并发收集周期都会很长,回收到的内存空间持续小于期间并发产生的浮动垃圾所占的空间,堆中剩余可腾挪的空间就越来越小了。
    [劣势如何解决呢?] :目前唯一的办法就是 尽可能地增加堆容量大小,获得更多喘息的时间。但是若要从根本上提升ZGC能够应对的对象分配速率,还是需要 引入分代收集,让新生对象都在一个专门的区域中创建, 然后专门针对这 个区域进行更频繁、更快的收集。Azul的C4收集器实现了分代收集后,能够应对的对象分配速率就比 不分代的PGC收集器提升了十倍之多。

  • ZGC还有一个常在技术资料上被提及的优点是支持“NUMA-Aware”的内存分配:
    【注】: NUMA(Non-Uniform Memory Access,非统一内存访问架构)是一种 为多处理器或者多核处理器的计算机所设计的内存架构。由于摩尔定律逐渐失效,现代处理器因频率发展受限转而向多核方向发展,以前原本在北桥芯片中的内存控制器也被集成到了处理器内核中,这样 每个处理器核心所在的裸晶(DIE)都有 属于自己内存管理器所管理的内存,如果要访问被其他处理器核心管理的内存,就必须通过InterConnect通道来完成,这要比访问处理器的本地内存慢得多。
  • 在NUMA架构下,ZGC收集器会优先尝试在请求线程当前所处的处理器的本地内存上分配对象,以保证高效内存访问。在ZGC之前的收集器就 只有针对吞吐量设计的Parallel Scavenge支持NUMA内存分配,如今ZGC也成为另外一个选择。
  • 在性能方面,尽管目前还处于实验状态,还没有完成所有特性,稳定性打磨和性能调优也仍在进行,但即使是这种状态下的ZGC,其性能表现已经相当亮眼,从官方给出的测试结果来看,用“令人震惊的、革命性的ZGC”来形容都不为过。
  • ZGC与Parallel Scavenge、G1三款收集器通过SPECjbb 2015的测试结果。在 ZGC的“弱项”吞吐量方面,以低延迟为首要目标的ZGC已经达到了以高吞吐量为目标Parallel Scavenge 的99%,直接超越了G1。 如果将吞吐量测试设定为面向SLA(Service Level Agreements)应用 的“Critical Throughput”的话,ZGC的表现甚至还反超了Parallel Scavenge收集器。
  • 而在ZGC的强项停顿时间测试上,它就毫不留情地与Parallel Scavenge、G1拉开了两个数量级的差距。不论是平均停顿,还是95%停顿、99%停顿、99.9%停顿,抑或是最大停顿时间,ZGC均能毫不费劲地控制在十毫秒之内,以至于把它和另外两款停顿数百近千毫秒的收集器放到一起对比,就几乎显示不了ZGC的柱状条,必须把结果的纵坐标从线性尺度调整成对数尺度才能观察到ZGC的测试结果。

总之,就到这里吧~ 对于这两个回收器的分析基本都是书上的,也没有太多新鲜的东西,但是要重点学其思想,加油!

JVM之(Shenandoah、ZGC收集器)(基于《深入理解Java虚拟机》之第三章垃圾收集器与内存分配策略)(下)相关推荐

  1. 《深入理解Java虚拟机》-----第3章 垃圾收集器与内存分配策略

    Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的"高墙",墙外面的人想进去,墙里面的人却想出来. 3.1 概述 说起垃圾收集(Garbage Collection,G ...

  2. 《深入理解java虚拟机》学习笔记四/垃圾收集器GC学习/一

    Grabage Collection      GC GC要完毕的三件事情: 哪些内存须要回收? 什么时候回收? 怎样回收? 内存运行时区域的各个部分中: 程序计数器.虚拟机栈.本地方法栈这3个区域随 ...

  3. 《深入理解Java虚拟机第3版》垃圾收集器与内存分配策略、虚拟机性能监控故障处理工具

    目录 往期博客:Java课堂篇3_初识JMM.常量池简单理解(字符串常量池.静态常量池.大整型常量池) 为什么要了解垃圾收集和内存分配? 如何判断对象已死? 引用计数算法 可达性分析算法 JDK1.2 ...

  4. 第三章 垃圾收集器与内存分配策略

    第三章 垃圾收集器与内存分配策略 前言: 3.1 概述 3.2 对象已死? 3.2.1 引用计数算法 3.2.2 可达性分析算法 3.2.3 再谈引用,四种引用类型 3.2.4 生存还是死亡 3.3 ...

  5. 【深入理解Java虚拟机学习笔记】第三章 垃圾收集器与内存分配策略

    最近想好好复习一下java虚拟机,我想通过深读 [理解Java虚拟机 jvm 高级特性与最佳实践] (作者 周志明) 并且通过写一些博客总结来将该书读薄读透,这里文章内容仅仅是个人阅读后简短总结,加强 ...

  6. java虚拟机读书笔记 第三章 垃圾收集器和内存分配策略

    java虚拟机读书笔记 第三章 垃圾收集器和内存分配策略 GC需要完成的三件事情:哪些内存需要回收.什么时候回收.如何回收 垃圾回收器在对堆进行回收前,首先要确定那些对象存活,哪些对象已经死去,判断的 ...

  7. 深入理解Java虚拟机(第三版)--经典垃圾收集器

    Serial收集器 Serial收集器是最基础.历史最悠久的收集器,曾经(在JDK 1.3.1之前)是HotSpot虚拟机新生代收集器的唯一选择.大家只看名字就能够猜到,这个收集器是一个单线程工作的收 ...

  8. 深入理解Java虚拟机(第三弹)- JVM 内存分配与回收策略原理,从此告别 JVM 内存分配文盲

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:打卡活动第二期来啦,100% 能获得奖品个人原创+1博客:点击前往,查看更多 虚拟机系列文章 深入理解 Java ...

  9. 笔记:深入理解JVM 第3章 垃圾回收器与内存分配策略

    1.对象是否已死 (1). 引用计数法:无法回收相互引用的对象,故JVM没有采用 例子: public class ReferenceObj { public ReferenceObj refObj; ...

最新文章

  1. USEARCH — 最简单易学的扩增子分析流程(中国总代理)
  2. 领导和管理?你一定要分得清!
  3. window下python扩展包大全
  4. java对象是 什么的集合_java持有对象-集合类
  5. css background-position
  6. 如何最大限度提升虚拟内存性能(组图)
  7. 插件translator_Zotero Jasminum 插件的更新记录
  8. Mac OSX 开机启动应用
  9. Microsoft.ACE.OLEDB.12.0' provider is not registered on the local machine
  10. WebSocket不同版本的三种握手方式以及一个Netty实现JAVA类
  11. php下载 微信头像图片_php 下载微信头像
  12. CloudStack(二)基础网络模式安装部署
  13. NodeJs 实现 WebSocket 即时通讯(简单版)
  14. 查找字符在字符串出现的次数
  15. 三线摆法测刚体转动惯量实验结论_关于刚体转动的前概念研究
  16. 云坤科技助建“i慧湖”区域创新服务平台
  17. ZOJ3594 Sexagenary Cycle
  18. linus torvalds
  19. 迟来的2021年终总结,2022已开始,Good luck every one! No pain No gain!
  20. java functionex_Atitit. atiJavaExConverter4js  新的特性

热门文章

  1. Android 百度地图 行动轨迹用彩色线画出实现方法
  2. 《崔庆才Python3网络爬虫开发实战教程》学习笔记(5):将爬虫爬取到的数据存储到TXT,Word,Excel,Json等文件中
  3. 迅雷同时下载的人数越多,BT下载越快的奥秘——另辟蹊径的P2P应用
  4. 串口通讯协议和RS-232的介绍以及USB/TTL转232模块的工作原理
  5. Excel文件写入和解析
  6. html导航跳转,css实现导航切换的实例代码
  7. java中的repo什么意思_java – Spring数据jpa repo,为什么需要接口服务和服务实现
  8. 【五六七人口普查】我国区县级人口基本情况
  9. Mysql中的flush privileges语句
  10. iOS音频系列(一)--音频基础