首页 > 编程语言 >深入理解Java虚拟机 --- 垃圾回收器

深入理解Java虚拟机 --- 垃圾回收器

时间:2024-11-08 13:20:00浏览次数:3  
标签:Java 收集器 对象 虚拟机 标记 --- 并发 GC 垃圾

Serial收集器

HotSpot虚拟机运行在客户端模式下的默认新生代收集器。

类型:单线程串行垃圾回收器

垃圾收集算法:复制算法

作用区域:新生代

特点:

1、只会用单个线程去完成垃圾收集工作,用户线程会STW,直到收集结束。

2、没有线程交互,专心做垃圾收集,获得最高的单线程收集效率。

ParNew收集器

类型:多线程并行垃圾回收器

垃圾收集算法:复制算法

作用区域:新生代

本质上就是Serial收集器的并行版本。

特点:

1、能与CMS收集器搭配使用。

Parallel Scavenge收集器

类型:多线程并行垃圾回收器

垃圾收集算法:复制算法

作用区域:新生代

特点:

1、是以吞吐量优先的垃圾回收器。(吞吐量=用户运行代码时间/用户运行代码时间+垃圾收集时间)

2、不能与CMS收集器搭配使用。

3、内置有一个 PS MarkSweep收集器,但是实现原理跟Serial实现基本一样。

为什么CMS只能和ParNew搭配使用,而不能和Parallel Scavenge搭配使用?

就性能来看,Parallel Scavenge它的性能会比ParNew的性能要好一些。

但是CMS只能和ParNew搭配使用,原因如下:

1、CMS的设计目标是低延迟,Parallel Scavenge的设计目标是高吞吐量。

2、Parallel Scavenge没有使用HotSpot的分代框架,而CMS使用了HotSpot的分代框架。(ParNew使用了HotSpot的分代框架)

Serial Old收集器

类型:单线程串型收集器

垃圾收集算法:标记-整理算法

作用区域:老年代

Parallel Old收集器

类型:多线程并行收集器

垃圾收集算法:标记-整理算法

作用区域:老年代

特点:

1、缓解了Parallel Scanvenge的尴尬局面。(在其出之前Parallel Scanvenge只能和Serial搭配使用)

CMS收集器

这款收集器是HotSpot虚拟机中第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程同时工作。

CMS收集器的关注点是尽可能缩短垃圾收集时用户线程的停顿时间。停顿时间越短(低延迟)就越适合与用户交互的程序,良好的响应速度能提升用户体验。

类型:多线程并发收集器

垃圾收集算法:标记-清除算法

作用区域:老年代

工作原理

image.png

  • 初始标记阶段:所有工作线程都会因为"Stop-the-World"机制而短暂暂停,这个阶段主要任务仅仅只是标记出GC Roots能直接关联到的对象

  • 并发标记阶段:从GC Roots的直接关联对象开始遍历整个对象图的过程,这个过程耗时较长但是不需要用户线程停顿。扫描完成后,这个过程可能会造成对象引用的改变。CMS使用了增量标记的技术,把改变的都引用发起者(黑色对象)存储起来。

  • 重新标记阶段:主要是处理并发阶段中存储起来的黑色对象,在STW的前提下,重新以他们为根进行标记。

  • 并发清除阶段:这个阶段清除掉标记阶段判断已经死亡的对象,释放内存空间。这个阶段可以与用户线程并发。

为什么采用标记-清除算法?

因为在清除过程中,GC线程是跟用户并发的,如果使用复制或标记-整理算法会造成对象内存地址的改变,那么会造成用户持有原先的引用而无法访问到对象的情况。

特点

1、触发CMS垃圾回收器时需要预留一定的空间来支持用户创建对象。

触发的阙值我们可以设置,但是在极端情况下,如果设置阙值过高,造成无法满足程序分配对象,那么虚拟机就会被迫启动Serial Old收集器来进行收集,这就会造成长时间的STW了。

2、会产生浮动垃圾

3、进行Full GC时,会进行内存碎片的整理

4、CMS对CPU资源非常敏感,在并发阶段时,虽然不会造成用户线程的STW,但是会因为占有CPU资源,而导致应用程序变慢,降低总吞吐量。

G1垃圾收集器

具有跨时代意义的垃圾收集器,设计思想:局部收集、基于Region的内存布局形式

设计目的:回收垃圾最大化。

作用区域:整个堆

垃圾收集算法:从整体上来看是标记-整理算法。但从两个Region的角度来看,是标记-复制算法。

Region

image.png

G1保留了年轻代和老年代的概念,但新生代和老年代不再是固定的了,而是一系列不需要连续的动态集合。

其中,Homongous区域主要用来存放大对象。G1认为只要大小超过了一半的Region区域的对象就称为大对象。大多数情况下,G1把Homongous当成老年代看待。

设置H区域的原因

之前遇到大对象且年轻代Eden空间不够的话,那么会将大对象放到老年代,那么这个大对象得等到Old GC或者Full GC才能得以清理。

而设置H区之后:1、避免了内存碎片 2、提高了回收大对象的效率

工作原理

  • 初始标记阶段:所有工作线程都会因为"Stop-the-World"机制而短暂暂停,这个阶段主要任务仅仅只是标记出GC Roots能直接关联到的对象

  • 并发标记阶段:从GC Root开始对堆中对象进行可达性分析,这个过程与用户线程并发执行。扫描完成后,这个过程中可能会存在对象引用的改变,G1使用原始快照(STAB)方式来避免出现漏标的情况。

  • 重新标记阶段:主要处理并发标记过程中的STAB记录,在STW的前提下,以他们为根进行重新标记。

  • 清除阶段:这个阶段需要用户线程STW。这个阶段清除掉标记阶段判断已经死亡的对象,释放内存空间。(使用的是复制算法)

问题

跨Region的引用问题:

当最开始我们使用分代模型的时候,只需要注意跨代引用,我们解决的思路是通过记忆集统计老年代到年轻代的引用即可。

而跨Region,我们就需要记录得跟复杂了。解题思路还是通过记忆集来记录跨Region引用,通过空间换时间的思路来避免全堆作为GC Root扫描。但是这个记忆集就需要每个Region都去维护自己独特的一个了,每个Region的记忆集不仅要记录我引用了谁,也要记录谁引用了我。

所以,记忆集造成了比较大的空间浪费。(10%~20%的堆空间浪费)

并发标记阶段如何避免漏标问题:

漏标:把存活的对象给他误删了。

G1使用了原始快照来解决漏标问题。当灰色对象要解开和白色对象的引用时,把白色对象记录下来。并在后面以他们为根,去扫描,让他们存活。(思路:就是以可能存在浮动垃圾的下,减少扫描的时间。)

Young GC & Mixed GC

G1通过构造一个可预测的时间模型,来在特定的时间内收获最高的垃圾。

那么在收集时,就不会只局限于年轻代了,当涉及多个代时,我们称为Mixed GC。

当只涉及年轻代时,我们还是称为Young GC。

ZGC垃圾收集器

概述

ZGC是JDK11推出的低延迟垃圾回收器。

设计目标:

1、停顿时间不超过10ms(低停顿)

2、停顿时间不会随着堆大小或者活跃对象的大小而增加。

适用场景:大内存低延迟服务

前者GC的痛点

前者的GC在STW这块都不是特别友善,所以在一些低延迟服务造成了性能困扰。

CMS(ParNew)与G1停顿时间瓶颈

ParNew和G1使用的都是标记-复制算法。

瓶颈定位→复制阶段中的转移阶段中复制对象。

复制阶段中转移阶段是STW的,转移阶段需要分配新内存和复制对象的成员变量。其中内存分配比较快,但是在复制一些复杂对象的时候耗时会比较长。

为什么转移阶段不能和用户并发执行呢?

主要是无法解决转移过程中精确定位对象地址的问题。如果并发执行,那么会导致用户访问不到具体对象。

ZGC的原理

全并发

ZGC采用标记-复制算法。不过ZGC在标记、转移和重定位阶段几乎都是并发的。

image.png

ZGC只有三个STW阶段:初始标记,再标记,初始转移。其中,初始标记和初始转移分别都只需要扫描所有GC Roots,其处理时间和GC Roots的数量成正比,一般情况耗时非常短;再标记阶段STW时间很短,最多1ms,超过1ms则再次进入并发标记阶段。即,ZGC几乎所有暂停都只依赖于GC Roots集合大小停顿时间不会随着堆的大小或者活跃对象的大小而增加。与ZGC对比,G1的转移阶段完全STW的,且停顿时间随存活对象的大小增加而增加

ZGC的技术

着色指针

image.png

着色指针是一种将信息存储在指针中的技术。

当应用程序创建对象时,首先在堆空间申请一个虚拟地址。同时会在M0、M1、Remapped地址空间分别申请一个虚拟地址,且三个虚拟地址对应同一个物理地址但这三个虚拟地址在同一时间只有一个空间有效

读屏障

Object o = obj.FieldA   // 从堆中读取引用,需要加入屏障
<Load barrier>
Object p = o  // 无需加入屏障,因为不是从堆中读取引用
o.dosomething() // 无需加入屏障,因为不是从堆中读取引用
int i =  obj.FieldB  //无需加入屏障,因为不是对象引用

ZGC中读屏障的代码作用:在对象标记和转移过程中,用于确定对象的引用是否满足条件,并做出对应的动作。

ZGC的并发处理过程

1、初始化:ZGC初始化之后,整个内存空间的地址视图被设置成Remapped。程序正常运行,在内存中分配对象,满足一定条件后垃圾回收启动,进入标记阶段。

2、并发标记阶段:第一次进入标记阶段时视图为M0,如果对象被GC标记线程或者应用线程访问过,那么就讲对象的地址视图从Remapped调整为M0。所以,在标记阶段之后,如果在M0就说明对象是活跃的,如果是在Remapped就说明对象是不活跃的。

3、并发转移阶段:标记结束后就进入转移阶段,此时地址视图再次被设置为Remapped,如果对象被GC标记线程或者应用程序访问过,那么会从M0调整为Remapped。

其实,在标记阶段存在两个地址视图M0和M1,上面的过程显示只用了一个地址视图。之所以设计成两个,是为了区别前一次标记和当前标记。也即,第二次进入并发标记阶段后,地址视图调整为M1,而非M0。

着色指针和读屏障技术不仅应用在并发转移阶段,还应用在并发标记阶段:将对象设置为已标记,传统的垃圾回收器需要进行一次内存访问,并将对象存活信息放在对象头中;而在ZGC中,只需要设置指针地址的第42~45位即可,并且因为是寄存器访问,所以速度比访问内存更快。

标签:Java,收集器,对象,虚拟机,标记,---,并发,GC,垃圾
From: https://www.cnblogs.com/ayu0v0/p/18534872

相关文章

  • 深入理解Java虚拟机 --- 内存分配与回收策略
    对象优先在Eden区分配大多数情况下,对象在Eden区进行分配。当Eden区没有足够的空间来进行分配时,就会触发YoungGC(MinorGC)。当触发YoungGC时,如果Survivor区不够放存活的对象,那么就会触发分配担保机制提前转移到老年代。大对象直接进入老年代大对象的问题:1、容易导致内存明......
  • 深入理解Java虚拟机 --- 类加载机制
    类的生命周期类的生命周期:加载→验证→准备→解析→初始化→使用→卸载类加载的时机关于在什么情况下需要需要开始类加载过程的第一个阶段"加载",虚拟机并没有进行强制约束,这点交给虚拟机的具体实现来自由把握。但严格规定了有且只有六种情况必须立即对类进行"初始化":(字节码......
  • 开源模型应用落地-glm模型小试-glm-4-9b-chat-tools使用(五)
    一、前言  GLM-4是智谱AI团队于2024年1月16日发布的基座大模型,旨在自动理解和规划用户的复杂指令,并能调用网页浏览器。其功能包括数据分析、图表创建、PPT生成等,支持128K的上下文窗口,使其在长文本处理和精度召回方面表现优异,且在中文对齐能力上超过GPT-4。与之前的GLM系列......
  • JavaLin第六章:JavaLin的访问管理和默认响应
    文章目录前言一、JavaLin的访问管理二、JavaLin的默认响应总结前言最近忙起来了,就没有更新了,实在不不好意思,最近会进行javalin的陆陆续续的更新,希望大家支持。在Java里面有很多框架,其中权限管理是一个非常重要的功能实现,对于每个用户设定该用户的角色,对应相应用户......
  • 复制下来就能跑:Java智能问答系统-介绍与代码实践 - 基于springboot_springai_国产大模
    本文的目的是在5分钟内能把智能问答系统的原理和实践讲明白代码可执行,复制粘贴即可,可以快速跑起来。智能问答系统简介智能问答系统是一种人工智能应用,它能够理解用户提出的问题,并通过自然语言处理技术来分析和理解问题的含义。随后,系统会在其知识库中搜索相关信息,以生成......
  • 庖丁解java(一篇文章学java)
    (大家不用收藏这篇文章,因为这篇文章会经常更新,也就是删除后重发) 一篇文章学java,这是我滴一个执念...当然,真一篇文章就写完java基础,java架构,java业务实现,java业务扩展,根本不可能.所以,这篇文章,就是一个索引,索什么呢?  请看下文...关于决定开始写博文的介绍......
  • 【补档】玄武550电源怎么样? - 约呼的回答 - 知乎
    【补档】玄武550电源怎么样?-约呼的回答-知乎约呼一个路过的图吧用户低预算整机非常推荐,高预算慎选。低预算整机非常推荐,高预算慎选。优点不少,缺点也有。先说优点:价格上来说便宜大碗,用料不错,部分型号反向虚标,比如550v4其实是按650w规格用料来做的,拆解视频一堆,相对透明,售......
  • MM--项目中遇到的一些问题记录
    采购订单:创建采购订单时,供应商对应的信息记录已失效,但是切换供应商之后价格不会自动更新成01.SU01-查看用户对应的采购缺省值  2.SPRO-维护采购缺省值 采购订单收货的时候,提示无符合条件的行项目经打断点发现是由于程序中判断时公司间的内部交易所导致的,根本原......
  • c语言入门学习这一篇就够了-知识点总结(三万字二级必看)
    C语言   C语言是中高级语言的代表之一,它是所有现代编程语言的基石,包括C++、Java、C#、Python、JavaScript、Swift等。C语言是学习其他编程语言的基础,因为它提供了对系统底层的精确控制,这使得它在开发操作系统、驱动程序、嵌入式系统、高性能计算等领域中有着不可替代的......
  • Me-LLaMA——用于医疗领域的新型开源大规模语言模型
    摘要大规模语言模型的出现是提高病人护理质量和临床操作效率的一个重大突破。大规模语言模型拥有数百亿个参数,通过海量文本数据训练而成,能够生成类似人类的反应并执行复杂的任务。这在改进临床文档、提高诊断准确性和管理病人护理方面显示出巨大的潜力。然而,像ChatGPT和......