HotSpot虚拟机垃圾回收算法及收集器

news2024/11/26 21:39:06

目录

一、对象引用

二、堆区和方法区回收

1. 堆区回收

2. 方法区回收 

三、垃圾回收算法

1. 算法总结

2. 算法相关细节 

四、垃圾收集器 

1. 新生代收集器

2. 老年代收集器

3. 混合式收集器G1

4. 低延迟收集器

五、参考资料


一、对象引用

        判定对象是否存活和引用离不开关系。JDK 1.2之后,有四种引用:强引用(Strongly Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference),其引用强度依次减弱(强软弱虚)

        若下代码所示,是四种引用的使用方式。

package com.cmmon.instance.gc;

import com.alibaba.fastjson.JSON;
import org.junit.jupiter.api.Test;

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;

/**
 * @author tcm
 * @version 1.0.0
 * @description JDK1.2的4种引用
 * @date 2023/5/24 17:25
 **/
public class ReferenceTest {

    /**
     * 强引用:new的对象,只要有引用链,则GC永远不会回收
     */
    @Test
    public void testStronglyReference() {
        // new创建对象,是强引用
        Map<String, String> map = new HashMap<>();
        map.put("name", "stronglyReference");

        System.out.println(JSON.toJSONString(map.keySet()));
    }

    /**
     * 软引用:内存不足时,GC会回收SoftReference所引用的对象
     * 适用场景:缓存
     */
    @Test
    public void testSoftReference() throws InterruptedException {
        // 软引用
        SoftReference<Map<String, String>> softReference = new SoftReference<Map<String, String>>(new HashMap<>());

        Map<String, String> map = softReference.get();
        if (map == null) {
            // 下面代码,这样做,仍然可能为null(概率很小)
            softReference = new SoftReference<Map<String, String>>(new HashMap<>());
        }
        map = softReference.get();

        map.put("name", "test");
        System.out.println(JSON.toJSONString(map.keySet()));
    }

    /**
     * 弱引用:内存是否足够都要回收,则只能生存到下一次GC回收WeakReference所引用的对象
     * 适用场景:Debug、内存监视工具等程序中(一般要求即要观察到对象,又不能影响该对象正常的GC过程)
     */
    @Test
    public void testWeakReference(){
        WeakReference<Map<String, String>> weakReference = new WeakReference<Map<String, String>>(new HashMap<>());
        Map<String, String> map = weakReference.get();

        if (map == null) {
            // 下面代码,这样做,仍然可能为null(概率很小)
            weakReference = new WeakReference<Map<String, String>>(new HashMap<>());
        }
        map = weakReference.get();

        map.put("name", "test");
        System.out.println(JSON.toJSONString(map.keySet()));
    }

    /**
     * 虚引用:无法通过虚引用来获取一个对象实例,目的回收时通知系统
     * 以下代码报错:java.lang.NullPointerException
     */
    @Test
    public void testPhantomReference(){
        PhantomReference<Map<String, String>> phantomReference = new PhantomReference<Map<String, String>>(new HashMap<>(), new ReferenceQueue());
        Map<String, String> map = phantomReference.get();

        if (map == null) {
            phantomReference = new PhantomReference<Map<String, String>>(new HashMap<>(), new ReferenceQueue());
        }
        map = phantomReference.get();
        map.put("name", "test"); // 报空指针异常
        System.out.println(JSON.toJSONString(map.keySet()));
    }

}

二、堆区和方法区回收

1. 堆区回收

        对象“死去”,说明没有任何引用指向该对象,即:对象没有被任何引用使用。那么,如何判定一个对象是否存活呢?对象存活算法有两种:引用计数算法、可达性分析算法(目前使用),如下图所示。

        目前HotSpot虚拟机采用可达性分析算法来判定对象是否存活,某对象没有任何引用链(Reference Chain) ,则对象“死去”,如下图所示。

        根据上述,哪些内容可作为GC Roots?如下图所示。

        同时,准确且完整的GC Roots至关重要。目前所有收集器在进行枚举GC Roots对象时,都需要暂停用户线程来获取整个堆区原始快照(SATB:Snapshot At The Beginning),以达到GC Roots的准确性;不同的收集器,存在跨代和跨区引用问题(记忆集解决),以达到完整。详细见收集器章节。

        一个对象最多经历两次标记,如下图所示。其中,finalize()方法在Object中,只能被调用一次,第二次调用时则失效,所以:拯救对象再次能活,可以在finalize()方法再次引用(不推荐使用),测试代码如下。

package com.cmmon.instance.gc;

/**
 * @author tcm
 * @version 1.0.0
 * @description TODO
 * @date 2023/4/19 9:53
 **/
public class FinalizeEscapeGC {

    public static FinalizeEscapeGC SAVE_HOOK = null;

    private static final int _1MB = 1024 * 1024;

    private byte[] space1 = new byte[4 * _1MB];

    public void isAlive() {
        System.out.println("是的,我还活着!");
    }

    /**
     * 系统只能调用一次,推荐不使用该方法
     * @throws Throwable
     */
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("进入finalize()方法");
        FinalizeEscapeGC.SAVE_HOOK = this;
    }

    public static void main(String[] args) throws InterruptedException {
        SAVE_HOOK = new FinalizeEscapeGC();

        // 第一次自救,则成功
        SAVE_HOOK = null;
        /*
            注意:若使用-XX:+DisableExplicitGC禁止人工触发GC,
                 则不会触发Full GC,更不会进入finalize()方法,SAVE_HOOK = null时,则已经死了
         */
        System.gc();
        Thread.sleep(1000 * 6);
        if (SAVE_HOOK != null) {
            SAVE_HOOK.isAlive();
        } else {
            System.out.println("我已经死了!");
        }

        // 第二次自救,则失败
//        SAVE_HOOK = null;
//        System.gc();
//        Thread.sleep(5000);
//        if (SAVE_HOOK != null) {
//            SAVE_HOOK.isAlive();
//        } else {
//            System.out.println("2我已经死了!");
//        }
    }

    /*
    执行结果:
        [GC (System.gc()) [PSYoungGen: 4592K->1173K(37888K)] 4592K->1181K(123904K), 0.0283448 secs] [Times: user=0.00 sys=0.00, real=0.04 secs]
        [Full GC (System.gc()) [PSYoungGen: 1173K->0K(37888K)] [ParOldGen: 8K->1042K(86016K)] 1181K->1042K(123904K), [Metaspace: 3060K->3060K(1056768K)], 0.0072510 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
        进入finalize()方法
        是的,我还活着!
        [GC (System.gc()) [PSYoungGen: 1310K->64K(37888K)] 2353K->1106K(123904K), 0.0005835 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
        [Full GC (System.gc()) [PSYoungGen: 64K->0K(37888K)] [ParOldGen: 1042K->1039K(86016K)] 1106K->1039K(123904K), [Metaspace: 3061K->3061K(1056768K)], 0.0057221 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
        2我已经死了!
        Heap
         PSYoungGen      total 37888K, used 1638K [0x00000000d6380000, 0x00000000d8d80000, 0x0000000100000000)
          eden space 32768K, 5% used [0x00000000d6380000,0x00000000d6519b40,0x00000000d8380000)
          from space 5120K, 0% used [0x00000000d8880000,0x00000000d8880000,0x00000000d8d80000)
          to   space 5120K, 0% used [0x00000000d8380000,0x00000000d8380000,0x00000000d8880000)
         ParOldGen       total 86016K, used 1039K [0x0000000082a00000, 0x0000000087e00000, 0x00000000d6380000)
          object space 86016K, 1% used [0x0000000082a00000,0x0000000082b03fe0,0x0000000087e00000)
         Metaspace       used 3068K, capacity 4486K, committed 4864K, reserved 1056768K
          class space    used 324K, capacity 386K, committed 512K, reserved 1048576K
     */

}

2. 方法区回收 

        方法区回收主要是废弃的常量、不再使用的类型。需要主要的是:JDK7版本方法区内的常量池、静态变量移至堆中;JDK8使用元空间(Metaspace)代替永久代,主要存储类信息。

三、垃圾回收算法

1. 算法总结

        如下表所示,垃圾回收算法总结。

算法

特点

标记-清除

(Mark-Sweep)

1.标记存活的对象,统一回收所有未被标记的对象

2.最基础的收集算法;

3.缺点:内存碎片化,大对象分配时没有足够连续空间,则提前触发GC

              对象数量增加时,标记和清除两个过程执行效率低

4.适用:老年代;

5.采用该算法收集器:CMS。

标记-复制

(Mark-Copy)

1.“半区复制”:内存按大小相等分两块

              (使用一块;存活对象复制到另一块);

2.“Appel式回收”:新生代分为一个Eden区、两个Survivor区

   (每次可用一个Eden区、一个Survivor区;存活对象复制到另一个Survivor区)

3.缺点:“半区复制”内存浪费大,缩小一半

              “Appel式回收”,默认90%可用(Eden:Survivor=8:1)

4.适用:新生代;

5.采用该算法收集器:Serial、ParNew。

标记-整理

(Mark-Compact)

1.标记后不直接清理,而是所有存活对象向内存空间一端移动(整理)后,再清除

2.整理时,需要移动对象,则必须暂停用户线程才能进行

 (注意:最新的ZGC、Shenandoah收集器使用读屏障解决该问题);

3.缺点:移动对象时,需暂停用户线程(Stop the World);效率低

4.适用:老年代;

5.采用该算法收集器:Parallel Old。

        需要注意的是,分代收集是上面三种算法的混合使用。分代收集存在跨代引用问题,用记忆集(Remembered Set)解决。如下图所示,是分代收集假说及注意问题。

2. 算法相关细节 

        算法的实现细节,如:GC Root枚举、安全点、安全区域、记忆集、写屏障、三色标记等,如下如图所示。 

        其中,从GC Roots扫描整个堆的对象图时,按照“是否访问过” 用三种颜色标记。标记整个堆的对象是很耗时,用户线程停顿时,导致用户体验差。那么,与用户线程并发来进行可达性分析时会出现问题,如下图所示。

        如下图所示,并发时的解决方案。

四、垃圾收集器 

        下图所示是HotSpot虚拟机的垃圾收集器,图中连线说明可搭配使用。其中JDK9废弃Serial + CMS、ParNew + Serial Old。

        垃圾收集器三大指标:内存占用(Footprint)、吞吐量(ThroughPut)、延迟(Latency),一款优秀的收集器最多同时达成两项。而理想中的收集器其收集速率与内存分配速率相当,而不是整堆清除

        虚拟机吞吐量(ThroughPut) =  CPU运行用户代码时间 / ( CPU运行用户代码时间 + CPU运行垃圾回收时间 )。停顿时间短适合用户交互或保证服务响应的程序,提升交互体验;吞吐量高适合运算量大的服务,提高利用资源效率。

        各类收集器并发情况,如下图所示。Shenandoah、ZGC两款只有初始标记、最终标记这部分是固定的,与堆容量、堆的对象数量没有关系。

1. 新生代收集器

新生代收集器

特点

Serial

1.JDK1.3之前新生代收集器;

2.单线程收集,整个GC过程必须暂停用户线程;

3.算法:标记-复制;

4.缺点:整个GC过程必须暂停用户线程。

ParNew

1.JDK5新生代收集器;

2.多线程收集,整个GC过程必须暂停用户线程;

3.算法:标记-复制;

4.缺点:整个GC过程必须暂停用户线程;

5.与Serial比较:多线程,其他都一样;

6.只与CMS老年代收集器联合使用

Parallel Scavenge

1.JDK7新生代收集器;

2.多线程收集,整个GC过程必须暂停用户线程

3.算法:标记-复制;

4.缺点:整个GC过程必须暂停用户线程;

5.Parallel Scavenge关注点是达到可控的吞吐量;

   CMS关注点尽可能缩短停顿时间。

2. 老年代收集器

3. 混合式收集器G1

4. 低延迟收集器

        低延迟收集:Shenandoah(OpenJDK支持)、ZGC(OracleJDK12支持),这两款收集器抛弃了分代收集、记忆集等概念。

五、参考资料

JVM垃圾收集器(很全很全) - 简书

关于垃圾收集算法与垃圾收集器ParNew与CMS_parnew收集器_秋天的一亩三分地的博客-CSDN博客

详解ZGC垃圾收集器-CSDN博客

69.G1垃圾回收的详细过程 -了解_simpleGq的博客-CSDN博客

G1垃圾回收器_赵军林的博客-CSDN博客

垃圾收集算法——分代收集算法(Generational Collection)。_分代收集算法根据各个年代的特点采用最适当的收集算法_孤芳不自賞的博客-CSDN博客https://www.cnblogs.com/yufengzhang/p/10571081.html 

为对象分配内存——TLAB_usetlab_chengqiuming的博客-CSDN博客

JVM调优常用参数_printreferencegc_point-break的博客-CSDN博客

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

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

相关文章

Unix/Linux编程:Unix domain socket

〇、前言 socket 是一种 IPC &#xff08;Inter-Process Communication&#xff0c;进程间通信&#xff09;方法&#xff0c;它允许位于同一主机&#xff08;计算机&#xff09;或使用网络连接起来的不同主机上的应用程序之间交换数据。通过使用Socket&#xff0c;开发人员可以…

在VSCode中使用LaTex,语法检测插件grammarly

整个文章分为以下几个内容&#xff0c;打 * 的是必须要安装的 LaTex 安装*VSCode 安装*在 VSCode 中配置 LaTexGrammarly语法检测插件 LaTex 安装* latex的下载安装可参考&#xff1a;LaTex&#xff08;2021&#xff09;安装教程 VSCode 安装* VSCode下载&#xff1a;VSCo…

带你用WePY框架提升开发效率

在小程序开发中&#xff0c;提高开发效率、优化代码质量和增强用户体验是每位开发者都追求的目标。而wepy作为一种基于Vue.js的小程序开发框架&#xff0c;提供了更好的开发体验和更高效的开发方式。本文将介绍wepy的基本功能和特性&#xff0c;分享一些实际的代码案例&#xf…

图像的匹配

2023.6.7 图像的匹配 模板匹配是一种在较大图像中搜索和查找模板图像位置的方法。为此&#xff0c;OpenCV 带有一个函数 cv.matchTemplate()。它只是在输入图像上滑动模板图像&#xff08;如在 2D 卷积中&#xff09;&#xff0c;并比较模板图像下的模板和输入图像的补丁。在…

STM32——CAN通信

1、CAN通信概述 STM32F103有两个CAN&#xff0c;都分别有自己的发送接收邮箱。 发送邮箱共有3个来发送报文&#xff0c;发送调度器根据优先级决定哪个邮箱的报文先被发送。 共有2个接收FIFO&#xff0c;每个FIFO都可以存放3个完整的报文。它们完全由硬件来管理。 CAN通信通过…

Mujoco 控制器接口(四)

目录 .1 简介 1.1 控制器实现 .2 sim.step() .3 实例 References .1 简介 control看到下面的图中就是mjtNum类型 mjtNum实际上就是浮点数 ctrl是底层的输入 外界扰动就是通过上图的qfrc和xfrc来添加的 1.1 控制器实现 实际上加上控制器就是xml里添加actuator joint就是…

算法提高-图论-单源最短路的建图方式

单源最短路的建图方式 单源最短路的建图方式AcWing 1129. 热浪AcWing 1128. 信使AcWing 1127. 香甜的黄油AcWing 1126. 最小花费AcWing 920. 最优乘车AcWing 903. 昂贵的聘礼 单源最短路的建图方式 AcWing 1129. 热浪 #include <iostream> #include <cstring>usi…

安装OpenWrt到电脑,安装包选哪个?

https://downloads.openwrt.org/releases/22.03.5/targets/x86/ releases发行&#xff0c;targets目标&#xff1b; —————————————— 64&#xff1a;具有64位功能的计算机&#xff1b; generic&#xff1a;一般的&#xff0c;通用的&#xff0c;仅32位计算机&…

学习Vue这一个就够

1、淘宝镜像 1: 解释一下 npm 为什么要配置淘宝镜像原因&#xff1a;因为node.js 默认使用的是国外的网站 。国内访问有一个跨国内局域网的操作。所以就会有时候很慢。这就跟为什么网站的静态资源有些会使用CDN 加速一样的淘宝镜像是什么&#xff1f;就是npm 很多的插件淘宝已经…

docker cgroub

docker 的资源管理 1、cpu的资源控制 一&#xff1a;设置cpu的资源上限 cd /sys/fs/cgroup/cpu/docker/ cpu 的占用量达到100% cpu 设置一半50% 2. 设置cpu资源占用比&#xff08;设置多个容器才有用&#xff09; docker run -itd --name c3 --cpu-shares 512 centos:7 do…

springboot+mybatis实现删除(二)

一&#xff0c;XML映射文件和动态SQL XML映射文件的名称与Mapper接口名称一致&#xff0c;并且将XML映射文件和Mapper接口放置在相同包下&#xff08;同包同名&#xff09;&#xff0c;右键创建目录/分隔&#xff0c;例&#xff1a;com/baidu/crm XML映射文件的namespace属性为…

工程训练 -江苏海洋大学-mooc-最终答案

这不点赞评论一下嘛&#xff1f;&#xff1f;&#xff1f;呜呜呜 判断题&#xff08;共217道&#xff09; 1.舂实模样周围及砂箱边或狭窄部分的型砂&#xff0c;通常采用砂舂的平头端舂砂。 2.造型时&#xff0c;分型面上通常使用的是面砂&#xff0c;覆盖模样的则使用背砂。 3…

【微服务】springboot整合swagger多种模式使用详解

目录 一、前言 1.1 编写API文档 1.2 使用一些在线调试工具 1.3 postman 1.4 swagger 二、swagger简介 2.1 背景 2.2 swagger优缺点 2.2.1 swagger优点 2.2.2 swagger缺点 2.2.3 swagger使用场景 三、swagger常用的几种整合模式 3.1 swagger2 3.2 knife4j 3.2.1 k…

《设计模式》之命令模式

文章目录 1、定义2、动机3、类结构4、优缺点5、总结6、代码实现(C) 1、定义 将一个请求封装为一个对象&#xff0c;从而使你可用不同的请求对客户端进行参数化&#xff1b;对请求排队或记录请求日志&#xff0c;以及支持可撤销的操作。 2、动机 在软件构建过程中&#xff0c…

虚拟内存和物理内存:概念、原理和应用

目录 概述1. 概念2. 原理3. 设计寻位原理4. 应用场景结论 概述 当你使用计算机时&#xff0c;内存是一个非常重要的资源。它用于存储正在运行的程序和数据&#xff0c;确保系统的正常运行。在计算机系统中&#xff0c;存在着虚拟内存和物理内存的概念&#xff0c;它们共同协作…

Docker Gitlab Container Registry配置

文章目录 前言一、Registry是什么二、步骤配置gitlab.rb文件修改docker-compose.yaml文档验证推送镜像 总结 前言 找了很多资料包括官网1都没有发现比较清楚的配置registry的方法&#xff0c;自己摸索了半天发现其实通过简单设置就能够配置好Container Registry。 之所以在题…

【Docker】3.Docker Registry

文章目录 Docker RegistryDocker Registry CommandImage Command NginxNginx System installNginx Config Container CommandCreate My DegistryBusyBox腾讯云镜像仓库搭建 Docker Registry 镜像仓库负责存储、管理、分发镜像&#xff0c;并且提供了登录认证的能力&#xff0c…

LeetCode_二叉树_DFS_中等_129.求根节点到叶节点数字之和

目录 1.题目2.思路3.代码实现&#xff08;Java&#xff09; 1.题目 给你一个二叉树的根节点 root &#xff0c;树中每个节点都存放有一个 0 到 9 之间的数字。 每条从根节点到叶节点的路径都代表一个数字&#xff1a;例如&#xff0c;从根节点到叶节点的路径 1 -> 2 -> …

【计算机网络】IP 地址处理函数

目录 1.struct sockaddr_in的结构 2.一般我们写的结构 3.常见的“点分十进制” 到 ” uint32_t 的转化接口 3.1. inet_aton 和 inet_ntoa &#xff08;ipv4&#xff09; 3.2. inet_pton 和 inet_ntop (ipv4 和 ipv6&#xff09; 3.3. inet_addr 和 inet_network 3…

人工智能-深度学习-科研神器推荐

根据知乎问题 有没有什么可以节省大量时间的 Deep Learning 效率神器&#xff1f; 的回答&#xff0c;筛选整理出一些深度学习科研神器。包括参数优化、数据可视化、模型部署蒸馏剪枝等。收录到 人工智能-深度学习-科研神器推荐https://​www.webhub123.com/#/home/detail?p4O…