【JVM】垃圾回收之对象已死

news2024/11/17 7:35:02

文章目录

  • 1. 概述
  • 2. 引用计数算法
  • 3. 可达性分析算法
  • 4. 引用的分类
    • 4.1 软引用的应用

本文参考:深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)

1. 概述

总所周知,垃圾收集器的任务就是将“死去”的对象回收。

那么,垃圾收集器在对堆进行回收前,第一件事,就是要确认这些对象是否还“存活”着。

这里有两种算法:

  1. 引用计数算法
  2. 可达性分析算法

2. 引用计数算法

引用计数算法就是在对象中添加一个引用计数器,每当有一个地方引用它的时候,计数器就加一;当引用失效的时候,计数器值就减一;任何时刻计数器为零的对象就是不可能再被使用的。因此垃圾收集器就可以将此对象回收。

垃圾收集器虽然需要占用额外的内存来进行计数,但是它的原理简单,判定效率也很高。不过Java虚拟机并没有选用引用计数算法来管理内存。

这是因为单纯的引用计数很难解决对象之间相互循环依赖的问题。

image-20230128181644604

比如有两个对象A和B,A对象引用B对象,B对象引用A对象,除此之外再无任何引用,但是实际上这两个对象不可能再被使用。如果使用引用计数算法,因为它们两个对象相互引用着对象,所以导致引用计数器都为1,因此垃圾收集器无法回收它们。


3. 可达性分析算法

可达性分析算法的思路就是通过一系列称为“GC Roots”的跟对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索的过程所走过的路径称为“引用链”

如果某对象到GC Roots间没有任何引用链相连,或者用图论的话来说从GC Roots到这个对象不可达,那么则证明此对象不可能再被使用

image-20230128191357640

图中object5object6object7虽然有关联,但是它们到GC Root不可达,因此可以判定为是可回收对象。

在Java中,固定可以作为GC Roots的对象包括以下几种

  1. 在虚拟机栈(栈帧中的本地变量表)中引用的对象,比如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等。
  2. 在方法区中类静态属性引用的对象,比如Java类的引用类型静态变量。
  3. 在方法区中常量引用对象,比如StringTable里的引用
  4. 在本地方法栈中的JNI(也就是Native方法)引用的对象
  5. Java虚拟机内部的引用,比如基本数据类型对应的Class对象,一些异常对象(NullPointException等),还有系统类加载器。
  6. 所有被同步锁(synchronized关键字)持有的对象
  7. 反映Java虚拟机内部的情况JMXBeanJVMTI中注册的回调、本地代码缓存等

除了这些,还可以根据用户所选用的垃圾收集器以及当前回收的内存区域不同,还可以有其他对象“临时性”地加入,共同构成完整的GC Roots


4. 引用的分类

在JDK1.2之前,如果reference类型的数据中存储的数值代表的是另外一块内存的起始地址,就称该reference数据是代表某块内存、某个对象的引用。

在JDK1.2之后,引用分为强引用、软引用、弱引用和虚引用。

  1. 强引用
    1. 指的是程序代码中的引用赋值,比如Object obj = new Object(),只要强引用关系还存在,垃圾收集器就永远不会回收掉被引用的对象。
  2. 软引用
    1. 软引用描述的是一些还有用,但非必须的对象
    2. 被软引用关联的对象,在系统将要发生内存溢出异常时(内存不住),触发垃圾回收。在垃圾回收之后,仍然内存不足,那么如果该对象没有被强引用,就会将这些对象进行回收。也就是第二次回收。
    3. JDK1.2之后提供了SoftReference实现软引用
    4. 可以配合队列来释放软引用本身
  3. 弱引用
    1. 弱引用也是用来描述那些非必须的对象
    2. 被弱引用关联的对象只能生存到下一次垃圾收集发生为止,无论内存是否足够,都会回收只被弱引用关联的对象。
    3. JDK1.2之后提供了WeakReference实现弱引用
    4. 可以配合队列来释放弱引用本身
  4. 虚引用
    1. 主要配合ByteBuffer使用,被引用对象回收时,会将虚引用入队,由Reference Handler线程调用虚引用相关方法释放直接内存。详情可看【JVM】详解直接内存_起名方面没有灵感的博客-CSDN博客
    2. JDK1.2之后提供了PhantomReference类来实现虚引用
  5. 终结器引用
    1. 无需手动编码,但其内部配合引用队列使用,在垃圾回收时,终结器引用入队(被引用对象暂时没有被回收),再由 Finalizer 线程通过终结器引用找到被引用对象并调用它的 finalize 方法,第二次 GC 时才能回收被引用对象

image-20230128204014313


4.1 软引用的应用

前提,添加VM Options——-Xmx20m -XX:+PrintGCDetails -verbose:gc

-XX:+PrintGCDetails -verbose:gc可以将垃圾回收的信息打印出来

private static final int _4MB = 10 * 1024 * 1024;
public static void main(String[] args) throws IOException {
    List<byte[]> list = new ArrayList<>();
    for (int i = 0; i < 5; i++) {
        list.add(new byte[_4MB]);
    }
    System.in.read();
}

执行上面代码的时候,很明显会出现堆内存空间不足,因此会抛出OutOfMemoryError

image-20230128205028210

但是改用软引用的话,就不会出现堆空间不足的情况了。

public static void soft() {
    // list --> SoftReference --> byte[]

    List<SoftReference<byte[]>> list = new ArrayList<>();
    for (int i = 0; i < 5; i++) {
        SoftReference<byte[]> ref = new SoftReference<>(new byte[_4MB]);
        System.out.println(ref.get());
        list.add(ref);
        System.out.println(list.size());

    }
    System.out.println("循环结束:" + list.size());
    for (SoftReference<byte[]> ref : list) {
        System.out.println(ref.get());
    }
}

image-20230128205631791

看控制台不难看出,在内存不足触发垃圾回收的时候,并不会第一时间将软引用关联的对象回收,而是在第二次垃圾回收和内存仍然不足的情况下,才会将软引用的对象回收。

可以使用队列将软引用移除

private static final int _4MB = 4 * 1024 * 1024;

public static void main(String[] args) {
    List<SoftReference<byte[]>> list = new ArrayList<>();

    // 引用队列
    ReferenceQueue<byte[]> queue = new ReferenceQueue<>();

    for (int i = 0; i < 5; i++) {
        // 关联了引用队列, 当软引用所关联的 byte[]被回收时,软引用自己会加入到 queue 中去
        SoftReference<byte[]> ref = new SoftReference<>(new byte[_4MB], queue);
        System.out.println(ref.get());
        list.add(ref);
        System.out.println(list.size());
    }

    // 从队列中获取无用的 软引用对象,并移除
    Reference<? extends byte[]> poll = queue.poll();
    while( poll != null) {
        list.remove(poll);
        poll = queue.poll();
    }

    System.out.println("===========================");
    for (SoftReference<byte[]> reference : list) {
        System.out.println(reference.get());
    }

}

在这里插入图片描述


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

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

相关文章

自动驾驶感知——毫米波雷达

文章目录1. 雷达的基本概念1.1 毫米波雷达分类1.2 信息的传输1.3 毫米波雷达的信号频段1.4 毫米波雷达工作原理1.4.1 毫米波雷达测速测距的数学原理1.4.2 毫米波雷达测角度的数学原理1.4.3 硬件接口1.4.4 关键零部件1.4.5 数据的协议与格式1.5 车载毫米波雷达的重要参数1.6 车载…

94. BERT以及BERT代码实现

1. NLP里的迁移学习 使用预训练好的模型来抽取词、句子的特征 例如word2vec 或语言模型 不更新预训练好的模型需要构建新的网络来抓取新任务需要的信息 Word2vec忽略了时序信息&#xff0c;语言模型只看了一个方向Word2vec只是抽取底层的信息&#xff0c;作为embedding层&…

数据太多?3款免费数据分析软件,分分钟解决

本文分享下我在做数据分析时用过的几个简单易上手的数据可视化软件。 先放上目录&#xff1a; 数据统计收集类——简道云数据图表美化类——图表秀数据开发类——Echart 01 简道云 https://www.jiandaoyun.com/ 适用于&#xff1a;想要“简单易上手”适合业务人员&#xff0…

TF-A源码移植的过程

1.解压标准 tf-a 源码包&#xff1a; tar xfz tf-a-stm32mp-2.2.r2-r0.tar.gz 2.将 ST 官方补丁文件打到 tf-a 源码中&#xff1a; 3.配置交叉编译工具链&#xff1a; 进入~/FSMP1A/tf-a-stm32mp-2.2.r2-r0$ 目录下&#xff0c;打开Makefile.sdk 将如下内容进行更改 4.复制设…

【前端设计】监控顺序返回型总线超时的计时器模块设计

前言 总线超时检查机制是系统中必要的模块设计&#xff0c;用于在总线无法返回response时能够及时上报中断。从理论上分析&#xff0c;如果总线发生了诸如挂死或者物理损坏等超时行为&#xff0c;无论计时器上报timeout的时间偏大还是偏小&#xff0c;都是一定可以上报中断的。…

Xilinx MicroBlaze系列教程(适用于ISE和Vivado开发环境)

本文是Xilinx MicroBlaze系列教程的第0篇文章。这个系列文章是我个人最近两年使用Xilinx MicroBlaze软核的经验和笔记,以Xilinx ISE 14.7和Spartan-6,以及Vivado 2018.3和Artix-7为例,介绍MicroBlaze软核、AXI系列IP核的软硬件使用,希望能帮助到更多的人。 MicroBlaze是Xil…

什么是有限元分析?能用来干什么

您是否想过工程师和制造商如何测试他们设计的耐用性、强度和安全性&#xff1f;如果您看过汽车广告&#xff0c;您可能会相信工程师和设计师不断地破坏他们的产品以测试其强度。您可能会得出结论&#xff0c;制造商会重复此过程&#xff0c;直到设计能够承受巨大的损坏并达到可…

夜游经济:夜景“亮化”,形象“美化”,经济“活化”

复杂的国际形势之下&#xff0c;扩大国内消费需求&#xff0c;激发消费市场潜力&#xff0c;堪称疫后经济复苏振兴的“金钥匙”。这一背景下&#xff0c;大力发展夜游经济&#xff0c;成为提振国内消费需求、促进城乡居民就业、拉动经济复苏增长的重要突破口。去年以来&#xf…

无法超越的100米_百兆以太网传输距离_网线有哪几种?

对网络比较了解的朋友&#xff0c;都知道双绞线有一个“无法逾越”的“100米”传输距离。无论是10M传输速率的三类双绞线&#xff0c;还是100M传输速率的五类双绞线&#xff0c;甚至1000M传输速率的六类双绞线&#xff0c;最远有效传输距离为100米。在综合布线规范中&#xff0…

Qt下实现欧姆龙PLC 串口发送HOSTLINK(FINS)模式

文章目录前言一、HOSTLINK协议说明二、校验码&#xff08;FCS&#xff09;计算三、示例完整代码四、下载链接总结前言 本文讲述了Qt下模拟串口调试工具发送HOSTLINK&#xff08;FINS&#xff09;模式&#xff0c;主要进行了HR保持区的字和位的读写&#xff0c;对HOSTLINK协议中…

记一次CPU飚高以及排查过程

一.cpu突然飚高 收到系统频发的cpu超过90%的告警.虽然是在非线上环境出现.接到告警后第一反应还是去重启了机器,重启后cpu如期的下降了下来.以为能高枕无忧,不过一会儿还是收到了告警. 二.排查 2.1 top 指令查看物理机进程id 申请了堡垒机权限登上机器 top指令后.如下确实发…

微服务,Docker, k8s,Cloud native 云原生的简易发展史

微服务发展史 2005年&#xff1a;Dr. PeterRodgers在Web ServicesEdge大会上提出了“Micro-Web-Services”的概念。2011年&#xff1a;一个软件架构工作组使用了“microservice”一词来描述一种架构模式。2012年&#xff1a;同样是这个架构工作组&#xff0c;正式确定用“micr…

万字详解 C 语言文件操作

目录 一、什么是文件&#xff1f; 1.1 - 文件和流的基本概念 1.2 - 文件的分类 1.3 - 文件名 二、缓冲文件系统和非缓冲文件系统 三、文件指针类型 四、文件的打开和关闭 4.1 - fopen 4.2 - fclose 五、文件的顺序读写 5.1 - 字符输出函数 fputc 5.2 - 字符输入函数…

【Kubernetes 企业项目实战】06、基于 Jenkins+K8s 构建 DevOps 自动化运维管理平台(上)

目录 一、k8s 助力 DevOps 在企业落地实践 1.1 传统方式部署项目为什么发布慢&#xff0c;效率低&#xff1f; 1.2 上线一个功能&#xff0c;有多少时间被浪费了&#xff1f; 1.3 如何解决发布慢&#xff0c;效率低的问题呢&#xff1f; 1.4 什么是 DevOps&#xff1f; …

【JavaScript】原型链

文章目录构造函数原型对象访问机制内置构造函数一切皆对象原型链构造函数 - 本质还是一个函数- 和 new 关键字连用- 特点1. 自动创建一个对象2. 自动返回一个对象3. 让函数的this指向这个对象 书写构造函数的时候1. 属性写在函数内2. 方法写在原型上构造函数的不合理 把方法写在…

Android studio 护眼模式配置、字体大小设置、内存大小设置等各类疑难杂症

Android studio 4.1 1、左边目录栏颜色配置&#xff1a; 2、代码编辑区域背景色设置 3、控制台背景色设置 4、菜单栏、工具栏、左边栏字体大小设置 5、编码区字体大小设置 6、修改内存大小、显示内存 例如&#xff1a;修改android-studio/bin/studio.vmoptions studio64.vmop…

NANK南卡护眼台灯Pro评测,护眼台灯天花板般存在!

现代大环境下&#xff0c;生活节奏和压力的不断加快加重&#xff0c;如今的手机、电脑屏幕以及长时间的工作学习都会出现用眼过度的问题&#xff0c;久而久之&#xff0c;各种眼睛刺痛、干涩、肿胀等近视问题接踵而至。为了保障自己的健康&#xff0c;近些年&#xff0c;人们纷…

回归预测 | MATLAB实现SSA-CNN麻雀算法优化卷积神经网络多输入单输出

回归预测 | MATLAB实现SSA-CNN麻雀算法优化卷积神经网络多输入单输出 目录回归预测 | MATLAB实现SSA-CNN麻雀算法优化卷积神经网络多输入单输出预测效果基本介绍模型描述程序设计参考文献预测效果 基本介绍 MATLAB实现SSA-CNN麻雀算法优化卷积神经网络多输入单输出。 1 .data为…

基础课程11:调试工具

目标 有时事情不会按照预期进行&#xff0c;从总线(如果有的话)检索到的错误消息不能提供足够的信息。幸运的是&#xff0c;GStreamer附带了大量调试信息&#xff0c;这些信息通常会提示问题可能是什么。本教程展示了: 如何从GStreamer获取更多调试信息。 如何将自己的调试信…

电力电子器件简介

文章目录1、二极管2、BJT3、晶闸管&#xff08;SCR&#xff09;4、TRIAC5、GTO&#xff08;全控器件&#xff09;6、功率MOSFET&#xff08;开关速度快、电压驱动更容易&#xff09;7、IGBT8、总结![在这里插入图片描述](https://img-blog.csdnimg.cn/1d309b3d449040788c6437f8…