JVM-垃圾回收器

news2025/1/11 5:55:08

垃圾回收器

Serial

新生代垃圾回收器,单线程,会产生STW(Stop The World),采用拷贝算法。

它在停止线程时,并不是直接将线程强行停止,而是等线程运行到一个安全点(Safe Point)的时候才停止。

JDK刚诞生时的垃圾回收器,那个时候内存都比较小,所以停顿时间也不长。但如果现在的机器再用这个垃圾回收器,停顿时间就不可接受了,所以这个目前来说不怎么用了。

Serial Old

配合Serial回收器使用,用在老年代,特点同上。采用标记清除或者标记压缩算法。也基本不用了。

Parallel Scavenge

很多JDK版本的默认垃圾回收器,作用于新生代,采用拷贝算法。也会STW。只是STW之后,通过多线程来清理垃圾。

Parallel Old

一般和Parallel Scavenge配套使用,作用与老年代,采用标记压缩算法。

ParNew

在Parallel Scavenge基础上的新垃圾回收器,作用于新生代,大部分特点和Parallel Scavenge差不多。只是针对和CMS的配合他做了增强。所以他更适合配合CMS使用。在CMS运行到某个特定阶段时,它会同时运行。

PS 和 PN区别的延伸阅读

CMS

JDK1.4开始引入,作用于老年代,采用concurrent mark sweep算法,头一个支持垃圾回收线程和工作线程可以同时进行的垃圾回收器。

由于CMS先天存在很多问题,所以现在没有一个版本默认是CMS,只能手工指定。

阶段

  1. 初始标记。
    1. 单线程。STW。
    2. 主要是收集GC roots直接引用的对象并标记这些对象为存活对象,这些对象可能在新生代或者老年代,即需要对整个堆进行扫描,不过由于直接引用的对象较少,故不需要消耗很长的时间;
  2. 并发标记。
    1. 与应用程序并发执行,从初始标记阶段所收集到的GC roots直接引用的对象出发,继续扫描查找和标记可达的对象,这个过程涉及到整个堆,即包括新生代和老年代,故需要耗费较长时间,所以与应用程序并发执行,不会对应用程序造成干扰
  3. 并发预清理
    1. 有的文档中会有这个过程,有的没有。
    2. 并发预清理主要是将新生代中在并发标记阶段中晋升到老年代的对象进行清理,减少新生代中对象的数量,减少下一阶段重新标记需要扫描的对象数量。并发预清理阶段默认执行时间不能超过5s,否则直接进入重新标记阶段,默认值5s可以通过JVM参数:-XX:CMSMaxAbortablePrecleanTime 来调整。除此之外,还可以通过JVM参数:-XX:+CMSScavengeBeforeRemark 来开启在进行重新标记Remark之前,强制对新生代执行一次MinorGC,从而减少重新标记阶段需要扫描的对象数量,减少Remark的执行时间;
  4. 重新标记。
    1. 多线程。STW。由于第二步运行了以后会产生新的垃圾,同时之前被标记为垃圾的也可能不在是垃圾。
    2. 由于并发标记阶段,应用程序在并发执行,会产生新的垃圾,同时之前被标记为垃圾的也可能不在是垃圾。故在该阶段暂停应用,然后对在并发标记阶段中状态发生了变化的对象进行重新标记,此过程涉及到整个堆中,即从GC roots引用的对象出发,对新生代和老年代中发生了变化的对象进行重新标记。具体remark阶段的执行时间,可以通过GC日志的CMS-remark日志内存来查看
  5. 并发清除。
    1. 将之前标记为垃圾的对象清理掉。
  6. 并发重置
    1. 此步骤也在部分文档中出现。
    2. 对该次CMS垃圾回收中的数据结构进行重置,以便下次进行CMS

标记算法

CMS标记使用的算法是:三色标记算法+Incremental Update

优缺点

  • CMS垃圾回收的特点是应用程序可以并发执行,所以应用停顿时间较短,实现了高响应时间的目的。
  • 应用线程和垃圾回收线程同时执行,需要消耗较多的CPU资源外
  • 由于是基于标记清除算法,故会造成内存碎片。
    • 为了解决内存碎片问题,CMS提供了JVM参数:-XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=1,来配置在进行了Full GC时,对老年代进行压缩整理,处理掉内存碎片,其中CMSFullGCsBeforeCompaction配置进行了多少次Full GC之后执行一次内存压缩。
    • 与Parallel一样,也可以通过:-XX:ParallelGCThreads来配置并行垃圾回收线程数。
  • Concurrent Mode Failure
    • 由于CMS在执行过程中是与应用程序并发执行的,如果在此过程中,应用程序需要在老年代分配空间来存放对象,而老年代此时没有足够的空闲空间,此时会触发Concurrent Mode Failure,之后会进行一次Full GC,老年代降级为使用Serial Old垃圾回收器,此时会暂停所有的应用线程的执行。

G1

介绍

官方文档

G1在JDK1.7出现,1.8成熟,1.9成为默认垃圾回收器。英语:Garbage First Garbage Collector (G1 GC)

G1是一个分代的,增量的,并行与并发的标记-复制垃圾回收器。它的设计目标是为了适应现在不断扩大的内存和不断增加的处理器数量,进一步降低暂停时间(pause time),同时兼顾良好的吞吐量。

G1还有一个及其重要的特性:软实时(soft real-time)。所谓的实时垃圾回收,是指在要求的时间内完成垃圾回收。“软实时”则是指,用户可以指定垃圾回收时间的限时,G1会努力在这个时限内完成垃圾回收,但是G1并不担保每次都能在这个时限内完成垃圾回收。通过设定一个合理的目标,可以让达到90%以上的垃圾回收时间都在这个时限内。

特点

  • 并发收集,标记算法:三色标记
  • 压缩空闲空间不会延长GC的暂停时间
  • 更易预测的GC暂停时间;
  • 适用不需要实现很高的吞吐量的场景

内存模型

分区(Region)

在这里插入图片描述

上图摘自官网,可以看到G1和之前垃圾回收器的分代模型完全不一样!

G1将Java堆划分为多个大小相等的独立区域(Region)。JVM目标是不超过2048个Region,实际可以超过该值,但是不推荐。每个分区的大小从1M到32M不等,但是都是2的冥次方。一般Region大小约等于堆大小除以2048,比如堆大小为4096M,则Region大小为2M,当然也可以用参数"-XX:G1HeapRegionSize"手动指定Region大小,但是推荐默认的计算方式。

每个分区可能是年轻代也可能是老年代,但是在同一时刻只能属于某个代。年轻代、幸存区、老年代这些概念还存在,成为逻辑上的概念,这样方便复用之前分代框架的逻辑。在物理上不需要连续,则带来了额外的好处。有的分区内垃圾对象特别多,有的分区内垃圾对象很少,G1会优先回收垃圾对象特别多的分区,这样可以花费较少的时间来回收这些分区的垃圾,这也就是G1名字的由来,即首先收集垃圾最多的分区。

新生代其实并不是适用于这种算法的,依然是在新生代满了的时候,对整个新生代进行回收。整个新生代中的对象要么被回收、要么晋升。至于新生代也采取分区机制的原因,则是因为这样跟老年代的策略统一,方便调整代的大小。G1还是一种带压缩的收集器,在回收老年代的分区时,是将存活的对象从一个分区拷贝到另一个可用分区,这个拷贝的过程就实现了局部的压缩。

默认年轻代对堆内存的占比是5%,如果堆大小为4096M,那么年轻代占据200MB左右的内存,对应大概是100个Region,可以通过“-XX:G1NewSizePercent”设置新生代初始占比。在系统运行中,JVM会不停的给年轻代增加更多的Region,但是最多新生代的占比不会超过60%,可以通过“-XX:G1MaxNewSizePercent”调整。年轻代中的Eden和Survivor对应的region也跟之前一样,默认8:1:1,假设年轻代现在有1000个region,eden区对应800个,s0对应100个,s1对应100个。

巨形对象(Humongous Object)

一个大小达到甚至超过分区大小一半的对象称为巨型对象(Humongous Object)。当线程为巨型分配空间时,不能简单在TLAB进行分配,因为巨型对象的移动成本很高,而且有可能一个分区不能容纳巨型对象。因此,巨型对象会直接在老年代分配,所占用的连续空间称为巨型分区(Humongous Region)。G1内部做了一个优化,一旦发现没有引用指向巨型对象,则可直接在年轻代收集周期中被回收。

巨型对象会独占一个、或多个连续分区,其中第一个分区被标记为开始巨型(StartsHumongous),相邻连续分区被标记为连续巨型(ContinuesHumongous)。由于无法享受Lab带来的优化,并且确定一片连续的内存空间需要扫描整堆,因此确定巨型对象开始位置的成本非常高,如果可以,应用程序应避免生成巨型对象。

卡片Card

由于老年代对象也可能指向新生代,所以做YGC时,需要扫描整个OLD区,效率非常低。因此设计了CardTable

在每个分区内部又被分成了若干个大小为512 Byte卡片(Card),标识堆内存最小可用粒度所有分区的卡片将会记录在全局卡片表(Global Card Table)中,分配的对象会占用物理上连续的若干个卡片,当查找对分区内对象的引用时便可通过记录卡片来查找该引用对象(见RSet)。每次对内存的回收,都是对指定分区的卡片进行处理。结构上,Card Table用BitMap来实现

如果一个OLD区的Card中有对象指向Y区,就将它设为Dirty,下次扫描时,只需要扫描Dirty Card

CSet

CSet = Collection Set,用于记录堆中可被回收的分区的集合。等到回阶段时,会从CSet中查找可回收的分区。

在CSet中存活的数据会在GC过程中被移动到另一个可用分区,CSet中的分区可以来自Eden空间、survivor空间、或者老年代CSet会占用不到整个堆空间的1%大小。

RSet

RememberedSet,记录了其他Region中的对象到本Region的引用。RSet的价值在于使得垃圾收集器不需要扫描整个堆找到谁引用了当前分区中的对象,只需要扫描RSet即可。

缺点:每个分区存储这种指针,大概要占10%的空间,所以到了ZGC中取消了这个RSet。

垃圾回收流程

  1. 年轻代垃圾收集. 当年轻一代的占用率达到某个阈值时,就开始年轻代的垃圾回收,G1开始并发收集年轻代。
  2. 并发收集阶段. 并发标记确定了在下一个空间回收阶段中要保留的内存中所有当前可达的(活动)对象。标记尚未完全完成时,年轻代的垃圾收集同时开始时进行.
  3. 初始标记(Initial Mark)这个过程共用了Minor GC的暂停,这是因为它们可以复用root scan操作。虽然是STW的,但是过程很短
  4. Root区扫描 (Root Region Scan)即suvivor区域扫描.可能会有老年代的引用.
  5. 并发标记(Concurrent Mark)这个阶段从GC Roots 开始对heap中的对象标记,标记线程与应用线程并行执行,并且收集各个Region的存活对象信息
  6. 重新标记 (Remarking)和CMS类似,也是STW的。标记那些在并发标记发生变化的对象,使用了snapshot-at-the-beginning (SATB) 算法,比cms收集器remark阶段更高效.
  7. 清理阶段 (Cleanup) 不需要STW。如果发现Region里全是垃圾,这个阶段立马被清理掉。不全是垃圾的Region,并不会被立马处理,它会在Mixed GC阶段,进行收集。
MixedGC

当新对象产生的特别多时,YGC之后,空间依然达到了指定值(-XX:InitiatingHeapOccupancyPercent,默认:45%)启动MixedGC。

过程,类似CMS

  • 初始标记STW,标记根对象
  • 并发标记,从根对象往下找
  • 最终标记STW(重新标记)。找的过程中如果有新的变化,标记出来。
  • 筛选回收STW(并行)。对最需要回收,垃圾最多的区域进行回收。
FullGC

G1也会FullGC的,而且Java10以前是串行的,之后是并行。如何避免FGC?

  1. 扩内存
  2. 提高CPU性能(回收的快,业务逻辑产生对象的速度固定,垃圾回收越快,内存空间越大)
  3. 降低MixedGC触发的阈值,让MixedGC提早发生(默认是45%)
GC何时触发

YGC

  • Eden空间不足

  • 多线程并行执行

FGC

  • Old空间不足

  • System.gc()

三色标记算法

将对象标记成3种颜色,白色:未被标记对象,灰色:自身被标记,成员变量未被标记,黑色:自身和成员变量均已标记完成。

漏标

在remark过程中,黑色指向了白色如果不对黑色重新扫描,则会漏标会把白色D对象当做没有新引用指向从而回收掉
并发标记过程中,Mutator删除了所有从灰色到白色的引用,会产生漏标此时白色对象应该被回收

解决方案

打破上述两个条件之一即可
incrementalupdate – 增量更新,关注引用的增加,把黑色重新标记为灰色,下次重新扫描属性SATB snapshot at the beginning-关注引用的删除当B->D消失时,要把这个引用推到GC的堆栈,保证D还能被GC扫描到

为什么G1用SATB?

灰色>白色引用消失时,如果没有黑色指向白色引用会被push到堆栈下次扫描时拿到这个引用,由于有RSet的存在,不需要扫描整个堆去查找指向白色的引用,效率比较高SATB配合RSet,浑然天成

日志分析

ZGC

ZGC(The Z Garbage Collector)是JDK 11中推出的一款追求极致低延迟的实验性质的垃圾收集器,它曾经设计目标包括:

停顿时间不超过10ms;
停顿时间不会随着堆的大小,或者活跃对象的大小而增加;
支持8MB~4TB级别的堆(未来支持16TB)。

当初,提出这个目标的时候,有很多人都觉得设计者在吹牛逼。

但今天看来,这些“吹下的牛逼”都在一个个被实现。

基于最新的JDK15来看,“停顿时间不超过10ms”和“支持16TB的堆”这两个目标已经实现,并且官方明确指出JDK15中的ZGC不再是实验性质的垃圾收集器,且建议投入生产了。

ZGC内存布局

为了细粒度的控制内存的分配,和G1垃圾收集器一样,ZGC讲内存划分为小的分区,在ZGC中称为页面(page),ZGC中没有新生代、老年代分代的概念。ZGC支持3种页面,分别为小页面、中页面和大页面。其中小页面指的是2MB的页面空间,中页面指32MB的页面空间,大页面指受操作系统控制的大页。

当对象大小小于等于256KB时,对象分配在小页面。

当对象大小在256KB和4MB之间,对象分配在中页面。

当对象大于4MB,对象分配在大页面。

颜色指针算法

在JVM对象头的64bit中拿出3位,当对象的指针发生变化时,就在这里做记录。

总结

所有的垃圾回收器都会产生STW,只是时间长短而已,ZGC号称可以控制STW在10ms内。实际可以达到2ms。

在G1之前的垃圾回收器都进行了物理分代(年轻代,老年代),G1是逻辑分代。

垃圾回收和内存大小关系

Serial,几十M

PS,几个G

CMS,20G

G1,上百G

ZGC,4TB

常见组合

Serial+SerialOld,单线程的垃圾回收器,

Parallel Scavenge + Parallel Old,后面为了提高效率,诞生了PS(ParallelScavenge),为并发垃圾回收是因为无法忍受STW

ParNew+CMS,了配合CMS,诞生了PN,CMS是1.4版本后期引入,CMS是里程碑式的GC,它开启了并发回收的过程,但是CMS毛病较多,因此目前任何一个JDK版本默认是CMS

参考说明

本文章内容为个人笔记,部分内容来源于马士兵课程视频学习笔记。部分参考自其他相关博客。如有错误欢迎指正。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/704267.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

超级实用!详解Node.js中的lodash模块和async模块

文章目录 7. lodash 模块安装 Lodash数组处理对象处理函数式编程 8. async 模块安装 Async异步流程控制串行执行(Series)并行执行(Parallel)循环迭代(Each) 控制流程和错误处理瀑布流控制(Water…

强化学习:策略梯度法

策略梯度法的思路 之前我们是用表格的形式来表达策略,现在我们同样可以用函数来表达策略。之前学的所有的方法都是被称为 value-based,接来下学的叫 policy-based 。接下来我们来看一下 策略梯度法的思路。之前学的的策略全都是用表格来表示的&#xff0…

win 安装 C++运行环境 - MinGW

目录 一、下载二、安装四、检查是否安装成功五、参考文章 一、下载 官网地址:https://www.mingw-w64.org/downloads/ 1.1点击【MingW-W64-builds】 1.2点击【Github】 1.3下载 如果下载太慢,可以使用GitHub Proxy 代理加速 (ghproxy.com) 二、安装 …

c++为什么支持函数重载?

前言 在c语言中,函数名是不可以重复的,在同一作用域中函数名称都是唯一的。这也使得我们的函数调用充满了种种麻烦。 而c则支持通过函数重载解决了这个问题 函数重载:是函数的一种特殊情况,C允许在同一作用域中声明几个功能类似的…

如何在Microsoft Excel中移动列的位置

若要在 Excel 中移动列,请使用 shift 键或“插入剪切单元格”。你还可以神奇地更改所有列的顺序。 一、Shift 键 要在不覆盖现有数据的情况下快速移动 Excel 中的列,请按住键盘上的 shift 键。 移动单列: 1、首先,选择一列。 2、将鼠标悬停在所选内容的边界上。此时会出…

建立本地题库

建立试题库文件json 第一步: 按标准格式保存试题到本地,文件名为.json. import json import osimage_path os.path.join(C:\\, Users, 123, PycharmProjects, pythonProject1, test01, imges, jxn_0900.png)# 准备要保存的数据 data [{"id": 1,"…

基于深度学习的高精度动物园动物检测识别系统(PyTorch+Pyside6+YOLOv5模型)

摘要:基于深度学习的高精度动物园动物(水牛、斑马、大象、水豚、海龟、猫、奶牛、鹿、狗、火烈鸟、长颈鹿、捷豹、袋鼠、狮子、鹦鹉、企鹅、犀牛、羊、老虎)检测识别系统可用于日常生活中或野外来检测与定位动物园动物,利用深度学…

【Java可执行命令】(五)打包部署工具 jar:深入解析应用程序打包、分发和部署工具jar ~

Java可执行命令详解之jar 1️⃣ 概念2️⃣ 优势和缺点3️⃣ 使用3.1 语法格式:创建jar文件3.1.1 参数:-cf3.1.2 参数:-tf3.1.3 参数:-i3.1.4 参数:-v3.1.5 参数:-e 3.2 运行jar文件 4️⃣ 应用场景5️⃣ 注…

Qt实现电子商城系统

用Qt实现的电子商城系统: 1.功能包括: 1)管理员账户 2)用户管理 3)用户登录 4)商品管理 5)商品出售 6)软件打包 2.商品包括:程序源码、开发教程和程序讲解;也可以根据需求进行功能更改 3.试用链接 链接:https://pan.…

华硕无畏14pro设置指纹登录

正常的流程如下 打开一个文件夹,在左侧找到此电脑,点击右键,找到属性 在搜索框中搜索:设置指纹登录 找到指纹识别,左键点击即可展开,点击:添加手指 点击:开始 输入PIN(开…

Oracle Linux 迷途知返

Oracle Linux 6.9 发布了。Oracle 已经宣布发布 Oracle Linux 6 发行版的更新了。 新版本 Oracle Linux 6 Update 9,包括多个已更新的内核,以及两个新的 "Unbreakable Enterprise Kernel" 包和一个 "Red Hat Compatible Kernel" 包…

SAP 后台配置之FM基金管理篇

SAP FM基金管理后台配置及应用 1 二话不说先上后台配置,能跑通为先1.1 基础设置1.1.1 维护财务管理区1.1.2 分配财务管理区1.1.3 激活全局基金管理功能1.1.4 定义全局参数1.1.5 定义编号区间编号并分配1.1.6 创建更改层次变式1.1.3 激活科目分配元素1.1.4 设置允许空…

3 款适合您手机或平板电脑的最佳 Android 和 iOS 修复工具

让我们面对现实:技术不可能总是完美的,您的智能手机也是如此,尽管更容易爱上它。毕竟,它只是一台机器! 无论您使用的是 Android 还是 iOS 系统的智能手机或平板电脑,当您第一次带回家时,它都能…

搭建帮助中心需要用到哪些工具

随着企业的发展和客户需求的增加,为了提供更好的客户支持和服务,许多企业决定搭建帮助中心。一个完善的帮助中心可以为客户提供详细的产品信息、常见问题解答和使用指南等,帮助他们快速解决问题并提高客户满意度。在本文中,我们将…

优秀的测试工程师养成记,庸碌四年的点工,“我“要进阶了...

目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 怎么才能成为优秀…

运动耳机哪种戴着舒服、值得推荐的五款运动耳机

在运动中享受最佳声音和舒适感受。这些耳机不仅具备防汗、抗水、稳定舒适的特点,而且还拥有出色的音质和智能功能,帮助你充分释放潜能,突破自我。无论你是专业运动员还是热爱户外活动的人,这些运动耳机将成为你的最佳伙伴&#xf…

Spring AOP 源码探索 之 链式调用中的 ExposeInvocationInterceptor拦截器作用

文章目录 ExposeInvocationInterceptor 示例代码源码分析 extendAdvisorsmakeAdvisorChainAspectJCapableIfNecessary添加扩展拦截器的调用链作用示例总结相关学习路线 JAVA资深架构师成长路线->开源框架解读->Spring框架源码解读 ExposeInvocationInterceptor 从英文名字…

【Linux系列P5】gccg++与【动静态库】的美妙邂逅

前言 大家好吖,欢迎来到 YY 滴 Linux系列 ,热烈欢迎!本章主要内容面向接触过Linux的老铁,主要内容含 欢迎订阅 YY 滴Linux专栏!更多干货持续更新!以下是传送门! 订阅专栏阅读:YY的《…

exe的python文件打包

【步骤01】 【在命令行中用pip工具安装Pyinstaller模块】 pip install Pyinstaller 步骤02】 【切换命令行的路径到你要打包的Python源文件的文件夹路径下】 【下面是我要打包的Python源文件(散点坐标图.py)及其文件夹路径】 【步骤03】 【执行Pyi…

js使用replace保留两位小数,正则保留小数位,输入负数和小数正则自动校验

下面是方法,支持保留自定义小数位,这里使用的是截取,没有四舍五入,我的功能需求是只有两位小数的地方可以输入负数,所以只加在了num2的代码块里,如果你们需要所有数字都进行负号校验,可以吧if (isF)判断放在…