JAVA基础之垃圾收集器

news2024/11/15 12:13:20

一 JVM垃圾收集

分代收集思想

当前虚拟机的垃圾收集一般采用分代收集算法,这种算法本身没有创新性,只是根据对象存活周期的不同将内存分为几块。一般将java堆内存分为新生代和老年代,这样我们就可以根据不同年龄到的特点选择不同的垃圾收集算法。

比如:

  • 新生代中,每次收集都会有大量的对象(接近99%)被回收,所以可以选择复制算法,只需要付出少量对象的复制成本就可以完成每次垃圾收集。
  • 老年代中,对象的存活几率是比较高的,而且没有额外的空间对它进行分配担保,所以我们必须选择标记清除标记整理算法进行垃圾收集。注意标记清除或标记整理算法会比复制算法慢10倍以上;

二 常见的垃圾收集算法

标记清除算法

该算法分为标记和清除两个阶段:

  • 标记阶段:标记存活的对象(一般选择这种);
  • 回收阶段:统一回收所有未被标记的对象;

这是最基础的收集算法,比较简单,但是会带来两个明显的问题:

  1. 效率问题:如果标记的垃圾对象太多,效率会很低;
  2. 空间问题:标记清除后会产生大量不连续的碎片空间;

标记复制算法

为了解决标记清除算法效率问题,复制算法出现了,它将内存分为大小相同的两块,每次使用其中一块,当其中一块被使用完后,将还存活的对象复制到另一块空间,然后把已经使用的空间一次性清空。其实是用空间换时间的思想;

标记整理算法

根据老年代的特点出的一种标记算法,标记过程与标记清除算法一样,但是后续步骤不是直接对可回收对象进行回收,而且让所有存活对象向一端移动,然后清理掉分界线以外的内存;

三 垃圾收集器

如果说垃圾收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。

3.1 Serial收集器

Serial(串行)收集器是最基础、历史最悠久的垃圾收集器。是一款单线程收集器。它的单线程意味着他只会使用一条垃圾收集线程完成垃圾收集工作,更重要的是它在垃圾收集工作的时候必须暂停其他所有其他工作线程(Stop The Word),直到它收集完成;

  • 缺点:Stop The Word带来了不好用户体验;
  • 优点:简单而高效(与其他收集器的单线程相比),Serial收集器没有线程交互的开销,自然可以获得很高的单线程收集效率;

Serial Old收集器时Serial收集器的老年代版本,它同样是一个单线程收集器。它主要有两个用途:

  • 在JDK1.5及以前的版本中与Parallel Scavenge收集器搭配使用;
  • 作为CMS收集器的后备方案;

开启参数:-XX:UseSerialGC -XX:UseSerialOldGC

3.2 Parallel Scavenge收集器

Parallel Scavenge收集器其实是Serial收集器的多线程版本,除了使用多线程进行垃圾收集外,其他行为(控制参数、收集参数、回收策略)和Serial收集器类似。默认的收集线程数等于CPU核数,当然也可以通过参数(-XX:ParallelGCThreads)指定收集线程数,但是一般不推荐修改;

Parallel Scavenge收集器关注点是吞吐量(高效率的利用CPU)。CMS等垃圾收集器的关注点更多的是用户线程的停顿时间(提高用户体验)。所谓吞吐量=CPU用于运行用户代码的时间/CPU总消耗时间;Parallel Scavenge收集器提供了很多参数功用户找到最合适的停顿时间或最大吞吐量,如果对于收集器不是太了解,建议保持默认值;

Parallel Old收集器时Parallel Scagvenge收集器的老年代版本;使用多线程和标记整理算法。在注重吞吐量以及CPU资源的场景,都可以优先考虑ParallelScavenge收集器和Parallel Old收集器(JDK8默认的新生代和老年代收集器);

新生代采用标记复制算法,老年代使用标记整理算法;

-XX:+UseParallelGC(年轻代)   -XX:+UseParallelOldGC(老年代)

3.3 ParNew收集器

ParNew收集器跟Parallel收集器很类似,主要的区别在于它可以和CMS收集器配合使用。

ParNew收集器许多运行在Server模式下的虚拟机的首要选择,除了Serial收集器外,只有它能与CMS收集器配合工作;

新生代采用复制算法;

-XX:+UseParNewGC

3.5 CMS收集器

 
CMS收集器(Concurrent Mark Sweep)是一种以获取最短回收停顿时间为目标的收集器。它非常符合在注重用户体验的应用上使用,是HotSpot虚拟机第一款真正意义上的并发收集器,第一次实现了让垃圾收集器线程与用户线程同时工作。

CMS收集器时一种标记清除算法实现的,它的执行过程相对于前面集中垃圾收集器来说更加复杂一些。整个过程分为四个步骤:

  1. 初始标记:暂停所有的其他线程(STW),并记录下GcRoots直接能引用的对象,此阶段速度很快;
  2. 并发标记:从GCRoots直接关联的对象开始遍历整个对象图的过程,这个过程耗时较长,但是不需要暂停用户线程,用户线程与垃圾线程可以并发运行。因为用户线程继续运行,可能会导致已经标记过的对象状态发生了改变;
  3. 重新标记:此阶段是为了修正并发标记期间因为用户线程继续运行而导致标记产生变动的那一部分对象的标记记录(主要是处理漏标问题);这个阶段停顿时间一般会比初始标记阶段的时间稍长,远比并发标记阶段时间短。其中主要用到三色标记里的增量更新算法做重新标记。
  4. 并发清理:开启用户线程,同时GC线程开始对未标记的区域做清扫。这个阶段如果有新增对象会被标记为黑色不做任何处理(见下面三色标记算法详解)。
  5. 并发重置:重置本次GC过程中的标记数据。

CMS收集器是一款优秀的垃圾收集器,主要优点:并发收集、低停顿。但是它有下面几个明显的缺点:

  1. 对CPU资源敏感,会和应用程序抢资源;
  2. 无法处理浮动垃圾(在并发标记和并发清理阶段会产生新垃圾,这种垃圾只能等到下次GC时再清理)。
  3. 使用的标记清除算法会导致收集结束时产生大量空间碎片,当然可以通过参数-XX:UseCMSCompactAtFullCollection让JVM在执行完标记清除后再做整理;
  4. 执行过程不确定性,会存在上一次垃圾回收还没执行完成,然后新的垃圾回收又被触发的情况,特别是在并发标记和并发清理阶段出现,一边回收垃圾,系统一边运行,也许没有回收完成就会触发FullGC,也就是concurrent mode failure,此时会进入stop the word,使用serial old垃圾收集器来回收垃圾;

3.5.1 CMS的相关核心参数

  1. -XX:UseConcMarkSweepGC 使用CMS垃圾收集器
  2. -XX:ConcGCThreads 并发的GC线程数
  3. -XX:UseCMSCompactAtFullCollection FullGC之后做压缩整理(减少碎片)
  4. -XX:CMSFullGCsBeforeCompaction 多少次FullGC之后压缩一次,默认时0,代表每次FullGC后会压缩一次
  5. -XX:CMSInitiatingOccupancyFraction 当老年代使用率达到该比例时触发FullGC(默认92,单位百分比)
  6. -XX:CMSInitiatingOccupancyOnly 只使用设定的回收阈值(-XX:CMSInitiatingOccupancyFraction设置的值),如果不指定,JVM仅在第一次使用设定值,后续则会自动调整
  7. -XX:+CMSScavengeBeforeRemark 在CMS GC前启动一次minor gc,降低CMS GC标记阶段时的开销,一般CMS的GC耗时80%的时间都在标记阶段;
  8. -XX:+CMSParallelInitialMarkEnabled 在初始标记的时候多线程执行,缩短STW
  9. -XX:+CMSParallelRemarkEnabled 在重新标记的时候多线程执行,缩短STW

四 垃圾收集底层算法

4.1 三色标记

在并发标记的过程中,因为标记期间应用线程还在继续运行,对象间的引用可能会发生变化,多标和漏标的情况就可以发生。漏标的问题主要引入了三色标记算法来解决。

三色标记算法是把Gc roots可达性分析便利对象中遇到的对象,按照是否访问过这个条件标记成以下三种颜色:

  • 黑色:表示对象已经被垃圾收集器访问过,且这个对象的所有引用对象都应扫描过,它是安全存活的,如果有其他对象引用指向了黑色对象,无需重新扫描。黑色对象不可能直接(不经过灰色对象)指向某个白色对象。
  • 灰色:表示对象已经被垃圾收集器访问过,但这个对象至少存在一个引用还没被扫描过;
  • 白色:表示对象尚未被垃圾收集器访问过。显然可达性分析刚刚开始阶段,所有的对象都是白色的,若在分析结束阶段,仍然是白色的对象,即代表是不可达;

源码实例:

public class ThreeColorRemark {

    public static void main(String[] args) {
        A a = new A();
        //开始做并发标记
        D d = a.b.d;   // 1.读
        a.b.d = null;  // 2.写
        a.d = d;       // 3.写
    }
}

class A {
    B b = new B();
    D d = null;
}

class B {
    C c = new C();
    D d = new D();
}

class C {
}

class D {
}

4.2 存在的问题以及解决方案

4.2.1 多标-浮动垃圾

在并发标记过程中,如果由于方法运行结束导致部分局部变量(gcroot)被销毁,这个gcroot引用的对象之前又被扫描过(被标记为非垃圾对象),那么本轮GC不会收回这部分内存。这部分本应该回收但是没有回收到的内存,被称之为浮动垃圾。浮动垃圾不会影响垃圾回收的准确性,只需要等到下一轮垃圾回收才可以被清除;

4.2.2 漏标-读写屏障

漏标会导致被引用的对象被当成垃圾误删除,这是严重的bug,必须要解决,有两种解决方案:增量更新和原始快照;

  • 增量更新:当黑色对象插入新的指向白色对象的引用关系时,就将这个新插入的引用就下来,等并发扫描结束之后,再以这些记录的引用关系中的黑色对象为根,重新扫描一次,可以简化理解为,黑色对象一旦插入了指向白色对象的引用之后,就变回灰色对象了
  • 原始快照:当灰色对象要删除指向白色对象的引用关系时,就将这个要删除的引用记录下来,在并发扫描结束之后,再将这些记录的引用关系中的灰色对象为根,重新扫描一次,这样就能扫描到白色的对象,将白色对象直接标记为黑色(目的是让这种对象在本轮gc清理中能存活下来,待下一轮gc的时候重新扫描,这个对象可能是浮动垃圾);

无论是对引用关系记录的插入还是删除,虚拟机的记录操作都是通过写屏障实现的;

4.2.2.1 写屏障

给某个对象的成员变量赋值时,其底层代码大概长这样:

/**
* @param field 某对象的成员变量,如 a.b.d 
* @param new_value 新值,如 null
*/
void oop_field_store(oop* field, oop new_value) { 
    *field = new_value; // 赋值操作
} 

所谓的写屏障,其实就是指在赋值操作前后,加入一些处理(可以参考AOP的概念):

void oop_field_store(oop* field, oop new_value) {  
    pre_write_barrier(field);          // 写屏障-写前操作
    *field = new_value; 
    post_write_barrier(field, value);  // 写屏障-写后操作
}
写屏障实现SATB

当对象B的成员变量的引用发生变化时,比如引用消失(a.b.d = null),我们可以利用写屏障,将B原来成员变量的引用对象D记录下来:

void pre_write_barrier(oop* field) {
    oop old_value = *field;    // 获取旧值
    remark_set.add(old_value); // 记录原来的引用对象
}

 

写屏障实现增量更新

当对象A的成员变量的引用发生变化时,比如新增引用(a.d = d),我们可以利用写屏障,将A新的成员变量引用对象D记录下来:

void post_write_barrier(oop* field, oop new_value) {  
    remark_set.add(new_value);  // 记录新引用的对象
}
读屏障
oop oop_field_load(oop* field) {
    pre_load_barrier(field); // 读屏障-读取前操作
    return *field;
}

读屏障是直接针对第一步:D d = a.b.d,当读取成员变量时,一律记录下来: 

void pre_load_barrier(oop* field) {  
    oop old_value = *field;
    remark_set.add(old_value); // 记录读取到的对象
}

为什么G1使用SATB,CMS用增量更新解决三色标记的问题?

可能是:STAB相对于增量更新效率会高(当然STAB可能造成更多的浮动垃圾),因为不需要再重新标记阶段再次深度扫描被删除的引用对象,而CMS对增量引用的根对象会做深度扫描,G1因为很多对象都位于不同的reigon,CMS就一块老年代区域,重新深度扫描对象的话G1的代价会比CMS高,所以G1选择STAB不深度扫描对象,只是简单标记,等到下一轮GC再深度扫描;

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

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

相关文章

自动驾驶控制算法

本文内容来源是B站——忠厚老实的老王,侵删。 三个坐标系和一些有关的物理量 使用 frenet坐标系可以实现将车辆纵向控制和横向控制解耦,将其分开控制。使用右手系来进行学习。 一些有关物理量的基本概念: 运动学方程 建立微分方程 主要是弄…

Agent 智能体食用指南

Agent 智能体食用指南 三年前都在 ALL in AI,一年前都在 ALL in LLM,现在都在 ALL in AgentAutoGEN分析MetaGPT 分析RAG 分析MOE 多专家分析 三年前都在 ALL in AI,一年前都在 ALL in LLM,现在都在 ALL in Agent 科技圈焦点&…

智己汽车数据驱动中心PMO高级经理张晶女士受邀为第十三届中国PMO大会演讲嘉宾

全国PMO专业人士年度盛会 智己汽车科技有限公司数据驱动中心PMO高级经理张晶女士受邀为PMO评论主办的2024第十三届中国PMO大会演讲嘉宾,演讲议题为“规模化敏捷落地实践”。大会将于5月25-26日在北京举办,敬请关注! 议题简要: 2…

Spring Bean依赖注入-Spring入门(二)

1、SpringBean概述 在Spring中,一切Java对象都被视为Bean,用于实现某个具体功能。 Bean的依赖关系注入的过程,也称为Bean的装配过程。 Bean的装配方式有3种: XML配置文件注解Java类 Spring中常用的两种装配方式分别是基于XML的…

秋招之路 面经

这里参考一位很厉害的前辈所分享的他的嵌入式软件工程师秋招之路,自己详细的读了一下他的经历以及他的分享的秋招面试和项目经验的总结。 我的嵌入式软件工程师秋招之路(文末送福利)_嵌入式软件工程师 刷leetcode-CSDN博客 如何在面试中介绍…

UA时间控件,选择时分的控件

方式一&#xff1a; <sit-property sit-widget"sit-time-picker"sit-value"vm.StartTime"sit-format"HH:mm"sit-validation"{required: true}"sit-read-only"false">开始时间:</sit-property> 方式二&#xff…

go-cqhttp 机器人使用教程

API | go-cqhttp 帮助中心 参考 | go-cqhttp 帮助中心 机器人下载 发送消息 http://127.0.0.1:5700/send_msg?message_typeprivate&user_id911412667&message你好呀 检查端口是否打开 netstat -ano | findstr :5701 发送的请求 软件的dopost的解析 Overridepro…

【MySQL】Linux环境下MySQL基本操作

目录 一、登录进入MySQL 二、MySQL数据库的查看、创建、删除、重命名、拷贝操作 三、数据库下表的创建、删除、查看、修改&#xff08;增加、删除、更新字段/列&#xff0c;修改字段/列名&#xff09; 四、表中数据的插入、删除、查找、更新 一、登录进入MySQL mysql -u u…

【小浩算法cpp题解】判断环形链表

目录 前言我的思路思路一 &#xff08;哈希表记录链表的访问&#xff09;&#xff1a;思路二 &#xff08;双指针&#xff0c;快指针在前&#xff0c;慢指针在后&#xff09;&#xff1a; 我的代码运行结果 前言 前几天我写的代码&#xff0c;都是把所有的内容写在main函数里&…

国产生骨肉冻干品控好不好?热榜TOP5生骨肉冻干分享

对于新手养猫人来说&#xff0c;进口生骨肉冻干的高价常常让人疑惑&#xff0c;为何它能在养猫达人中赢得如此高的声誉&#xff1f;与国产生骨肉冻干相比&#xff0c;进口产品的价格高出数倍&#xff0c;那么这高昂的价格是否代表了其独特的价值&#xff0c;还是只是一个消费陷…

Ghost Buster Pro for Mac:强大的系统优化工具

Ghost Buster Pro for Mac是一款功能强大的系统优化工具&#xff0c;专为Mac用户设计&#xff0c;旨在提供全方位的系统清理、优化和维护服务。 Ghost Buster Pro for Mac v3.2.5激活版下载 这款软件拥有出色的垃圾清理能力&#xff0c;能够深度扫描并清除Mac上的无效目录、文件…

小白看完这篇文章也能踏进网安大门,成为网络安全工程师

前言 在当前的互联网环境下&#xff0c;不少人感受到了职场的寒冬与996工作制的压力。然而&#xff0c;无论环境如何变化&#xff0c;掌握实用的技术始终是保障职业稳定和发展的关键。特别是在网络安全这一领域&#xff0c;技术人才需求量大且持续增长。今天&#xff0c;我们将…

出海企业必备:Zoho Desk打造高效海外工单管理体系!

出海工单系统和常见的工单系统相比有什么不同呢&#xff1f;工单系统主要事帮助售前或者售后人员记录、处理、跟踪客户需求&#xff0c;不仅有利于企业内部管理的规范化&#xff0c;还能够有效提高客户服务质量。 工单系统可以帮助出海企业搭建统一的订单管理、售后服务、甚至…

微电子领域常见概念(六)化学键合

微电子领域常见概念&#xff08;六&#xff09;化学键合 化学键合是化学中一个非常基础且重要的概念&#xff0c;它描述了原子之间通过电子的相互作用形成的连接。可以进行以下分类&#xff1a; 1. 离子键合&#xff08;Ionic Bonding&#xff09; • 定义&#xff1a;离子键合…

(CVPR,2023)SAN:用于开放词汇语义分割的边缘适配网络

文章目录 相关论文相关资料摘要引言方法对视觉 token 的特征融合使用注意力偏差进行掩码识别分割图像生成 实验 相关论文 &#xff08;CVPR&#xff0c;2024&#xff09;SED&#xff1a;一个用于开放词汇语义分割的简单编解码器 &#xff08;CVPR&#xff0c;2024&#xff09;…

javaWeb项目-财务管理系统功能介绍

项目关键技术 开发工具&#xff1a;IDEA 、Eclipse 编程语言: Java 数据库: MySQL5.7 框架&#xff1a;ssm、Springboot 前端&#xff1a;Vue、ElementUI 关键技术&#xff1a;springboot、SSM、vue、MYSQL、MAVEN 数据库工具&#xff1a;Navicat、SQLyog 1、Springboot框架 …

计算机工作者学习平台

给大家分享了几个非常有用的学习平台&#xff0c;可以作为参考&#xff0c;具体为&#xff1a; 1.中国大学MOOC 中国大学MOOC_优质在线课程学习平台 2.牛客 牛客网 - 找工作神器|笔试题库|面试经验|实习招聘内推&#xff0c;求职就业一站解决_牛客网 3.CSDN https://www…

Learn ComputeShader 02 Multiple kernels

前面已经成功创建了第一个compute shader&#xff0c;并且使用它替换掉quad的材质的纹理&#xff0c;现在我们将要在计算着色器中创建多个kernel。 首先调整上次的计算着色器&#xff0c;让它显示为红色。 然后再次创建一个kernel&#xff0c;显示为黄色。 结果应该是这样的…

vulhub weblogic全系列靶场

目录 简介 需要使用的工具 CVE-2017-10271 0x00 漏洞产生原因 0x01 影响范围 0x02 漏洞地址 0x03 环境 0x04 漏洞复现 1. 手工 2. 漏洞利用工具 CVE-2018-2628 0x00 漏洞产生原因 0x01 影响范围 0x02 环境 0x03 漏洞复现 1.nmap扫是否是T3协议 2.漏洞检测&…

数据结构学习之路--玩转队列的内核知识(附C源码)

嗨嗨大家~我又来啦&#xff01;今天为大家带来的是与队列相关的知识。我们马上进入知识的海洋~ 目录 前言 一、队列 1 队列的概念 2 队列的实现 2.1 队列的定义 2.2 队列的初始化 2.3 队列的判空 2.4 入队 2.5 出队 2.6 取队头元素 2.7 取队尾元素 2.8 取…