JVM篇:垃圾回收

news2024/11/17 10:41:42

如何判断对象可以被回收

Java中对象能否被回收,是根据兑现是否被引用来决定的。如果对象被引用了,说明该对象还在使用,不允许被回收

main栈帧中demo变量存储着Demo实例对象的地址,与Demo实例对象建立了连接关系此时Demo实例对象可以通过demo访问,因此这个对象不能被回收。

当demo为null时,与Demo实例对象不存在连接关系,此时Demo对象就可以被回收了

引用计数法

引用计数是计算机编程语言中的一种内存管理技术,是指将资源(可以是对象、内存或磁盘空间等等)的被引用次数保存起来,当被引用次数变为零时就将其释放的过程。使用引用计数技术可以实现自动资源管理的目的。同时引用计数还可以指使用引用计数技术回收未使用资源的垃圾回收算法。需要注意的是,Java从来没有使用过该方法去实现GC。

一个对象被引用时计数器就进行+1,被引用次数减少一次时就-1。

public class demo8 {
    public static void main(String[] args) {
        String s1 = new String();  //s1的计数器加一
        String s2 = new String();  //s2的计数器加一
        s1 = s2;  //s2的计数器加一
        s1 = null;  //s1的计数器减一,此时为0,满足被释放条件。s2计数器减一,此时为1。
    }
}

优点

即时性:当引用变为零时会被直接回收。

无需遍历堆可以准确释放对应内存。

缺点

计数器开销大,每个对象都需要一个计数器,计数器的存储开销比较大,计数器增减时时间开销大

循环引用时无法做到垃圾回收

class A {
    B b;

    public A(B b) {
        this.b = b;
    }
}

class B {
    A a;

    public B(A a) {
        this.a = a;
    }
}

public class Main {
    public static void main(String[] args) {
        A a = new A(new B(null));//创建A对象引用,计数器加1
        B b = new B(a);//被对象b的属性a引用,计数器再加1
        a.b = b;
        a = null;//a不引用A了,计数器减1,但还被b.a引用。因此无法被回收。
        b = null;
    }
}

可达性分析算法

也可以称为根搜索算法、追踪性垃圾收集。所谓可达性分析就是,在垃圾回收之前,对堆中所有的对象进行扫描,看是否被根对象(一定不会被回收的对象)直接或间接引用,如果是则无法被回收,没有被引用则可回收。

GC Root对象是哪些?

  • 加入了synchronized同步锁的对象可以被当作GC Root
  • 本地方法栈内的引用的对象
  • 虚拟机栈中所引用的对象。如各个线程被调用的方法中使用到的参数、局部变量等。
  • 方法区中常量引用的对象。如String Table中里的引用的。
  • 基本数据类型对应的 Class 对象

简单讲,凡是被常量、静态变量、全局变量、运行时方法中的变量直接引用的对象,原则上不能被GC释放。

四种引用

强引用:上图中的实线,对于强引用对象,JVM何时都不会对其进行回收,即使是出现OOM错误(因此内存泄漏主要原因就是强引用对象无法被回收)。但是当强引用对象超过了作用域(暂时不理解)又或是显式将强引用赋值null(也就是解除了强引用关系)则可以被回收。

软引用SoftReference:对于弱引用对象,进行一次GC回收后,内存还是不足时会对其进行回收,否则可以保持存活。(可以用来实现缓存)

弱引用WeakReference:在执行GC回收时,内存即使充足也会被回收

虚引用PhantomReference:并不会决定对象的生命周期,虚引用并不能单独使用,而是要和引用队列一起使用,也不能通过虚引用来获取引用的对象。在JDK8版本中,当GC准备回收一个虚引用指向的对象时,会将虚引用存入引用队列,而被指向的对象不会被真正的回收。由另一线程去读取引用队列中的引用来执行被引用的对象回收之前的内存释放操作。例如Clearner类就是虚引用,在ByteBuffer类对象被回收之前,先进行直接内存的释放(释放操作在Clearner类中实现)再进行回收对象。但在JDK9之后虚引用不会对对象的生存产生任何影响。

需要注意的是,上图中的软引用与弱引用实际上也是一个对象,当引用的对象回收时,这些引用对象并不会被回收(因为被GC Root强引用),而是会被放入一个引用队列当中,当内存不足时,会通过遍历引用队列将这些已经没有引用对象的引用释放。

除了以上四种引用,还存在一种终结器引用,当GC回收一个重写了finalize()方法的对象时,JVM会给被回收对象创建一个终结器引用,同时将该引用放入引用队列,通过一个FinalizerHandler线程去处理引用队列当中的终结器引用,首先是判断被回收对象是否执行了finalize()方法,如果没有则执行,等到下一次GC时才会去释放该对象。

//-Xmx20m
public class demo9 {
    private final static int _4M = 4 * 1024 * 1024;

    public static void main(String[] args) {
        List<SoftReference<byte[]>> list = new ArrayList();
    	//list -->SoftReferencr -->byte
        for (int i = 0; i < 5; i++) {
            SoftReference<byte[]> softReference = new SoftReference<>(new byte[_4M]);
            System.out.println(softReference.get());
            list.add(softReference);
            System.out.println(list.size());
        }
        for (SoftReference<byte[]> softReference : list) {
            System.out.println(softReference.get());
        }
    }
}

运行结果如下

[B@1540e19d
1
[B@677327b6
2
[B@14ae5a5
3
[B@7f31245a
4
[B@6d6f6e28
5
null
null
null
null
[B@6d6f6e28

指定堆内存为20M,然后进行创建5次字节对象,将会进行内存回收,由运行结果可以看出来,前四次的软引用对象已经被释放,只留下了最后一个软引用对象。

可以看出在进行第二次垃圾回收时,连续回收了两次,因为第一次回收并没有回收充足的内存,因此执行第二次垃圾回收将软引用回收。

但是我们并不需要已经被回收的软引用,仍存在list队列中占用内存空间。我们可以将软引用关联到引用队列。

public class demo9 {
    private final static int _4M = 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++) {
            //指定绑定队列,只有软引用对象被回收的话,才会被加入引用队列
            SoftReference<byte[]> softReference = new SoftReference<>(new byte[_4M], queue);
            System.out.println(softReference.get());
            list.add(softReference);
            System.out.println(list.size());
        }

        Reference<? extends byte[]> poll = queue.poll();
        while (poll != null) {
            list.remove(poll);
            poll = queue.poll();
        }

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

运行结果

可以看到软引用对象被回收的软引用也被释放

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

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

相关文章

windows系统中如何安装tomcat

在 Windows 系统中安装 Tomcat 通常涉及以下步骤: 1.下载 Tomcat: 访问 Apache Tomcat 官方网站。在 "Download" 部分找到适用于 Windows 的最新版本的 Tomcat。下载 Core 二进制分发版(通常是.zip文件)。 2.解压 Tomcat 压缩包: 将下载的.zip文件解压到…

如何在前端项目里接入Sentry监控系统并通过企业微信通知

能不能让用户录个屏过来呀&#xff1f; 用户使用的是什么机型的手机&#xff1f; 用户使用的什么浏览器呀&#xff1f; 用户的网络是什么情况&#xff1f; … … 线上出现问题时&#xff0c;技术部和业务部同学之间的对话诸如此类…业务同学也很栓Q呀&#xff0c;硬着头皮去问客…

Java Swing桌面项目打包成可执行jar

前言 最近有需求&#xff0c;将Swing项目打包为一个可执行的jar包&#xff0c;遇见了一些问题&#xff0c;参考AI助手&#xff0c;解决了遇到的问题&#xff0c;也有一些亲身实践体会&#xff0c;记录一下。开发环境IntelliJ IDEA&#xff0c;JDK8&#xff0c;用kotlin语言实现…

257:vue+openlayers 实现动态点点网格

第257个 点击查看专栏目录 本示例介绍演示如何在vue+openlayers中实现动态网格,这里通过第三方插件ol-grid来实现。具体的请参考示例源代码和API 直接复制下面的 vue+openlayers源代码,操作2分钟即可运行实现效果 文章目录 示例效果图配置方式示例源代码(80行)相关API参考…

Discuz论坛搭建:Linux宝塔面板一键部署,固定地址畅享公网访问

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;网络奇遇记、Cpolar杂谈 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 &#x1f4cb;前言一. 安装基础环境二. 一键部署Discuz三. 安装cpolar工具四. 配置域名访问Discuz…

DS:单链表的实现(超详细!!)

创作不易&#xff0c;友友们点个三连吧&#xff01; 在博主的上一篇文章中&#xff0c;很详细地介绍了顺序表实现的过程以及如何去书写代码&#xff0c;如果没看过的友友们建议先去看看哦&#xff01; DS&#xff1a;顺序表的实现&#xff08;超详细&#xff01;&#xff01;&…

最新整理盘点全球最先进的几十种人工智能大模型,OpenChat:性能高达105.7%,第一个超越ChatGPT的开源模型?附开源代码地址

最新整理盘点全球最先进的几十种人工智能大模型,OpenChat:性能高达105.7%,第一个超越ChatGPT的开源模型?附开源代码地址 前几天开源模型第一还是是Vicuna-33B、WizardLM,这不又换人了。对于开源模型的风起云涌,大家见怪不怪,不断更新的LLM榜单似乎也没那么吸引人了。 U…

ajax点击搜索返回所需数据

html 中body设置&#xff08;css设置跟进自身需求&#xff09; <p idsearch_head>学生信息查询表</p> <div id"div_1"> <div class"search_div"> <div class"search_div_item"> …

C#用 DateAndTime.DateAdd方法和DateTime.Add(TimeSpan) 方法分别添加一段时间间隔

目录 一、基本方法 1.用 DateAndTime.DateAdd方法添加一段时间间隔 2.用DateTime.Add方法添加一段时间间隔 二、实例 1.实例1&#xff1a;用 DateAndTime.DateAdd方法 2.实例2&#xff1a;用DateTime.Add方法 一、基本方法 1.用 DateAndTime.DateAdd方法添加一段时间间隔…

Sentinel:微服务守护神的崛起

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 Sentinel&#xff1a;微服务守护神的崛起 前言Sentinel简介&#xff1a;微服务流控的新宠Sentinel工作原理 前言 想象一下你正在主持一场盛大的马拉松比赛&#xff0c;参赛者如潮水般汇聚&#xff0c…

linux中配置文件目录为什么用etc来命名

在早期的 Unix 系统中&#xff0c;/etc 目录的名称确实来源于单词 “etcetera” 的缩写&#xff0c;最初意味着 “其他”&#xff0c;用来存放杂项或者不属于其他特定目录的文件。然而&#xff0c;随着时间的推移&#xff0c;/etc 目录的用途逐渐演变并专门化。 在现代的 Linux…

husky结合commitlint审查commit信息

commintlint是一个npm包用来规范化我们的commit信息&#xff0c;当然这个行为的操作时期是在git的commit-msg生命周期期间&#xff0c;这一点当然是有husky来控制&#xff0c;需要注意的是commit-msg作为一个git生命周期会被git commit和git merge行为唤醒&#xff0c;并且可以…

C++版QT:分割窗口

目录 mainwindow.h mainwindow.cpp main.cpp Qt的分割窗口功能允许用户将一个窗口分割成多个区域&#xff0c;每个区域可以独立地显示不同的内容。这种功能在许多应用程序中非常有用&#xff0c;例如编辑器、浏览器和IDE等。 理解Qt的分割窗口&#xff0c;需要从以下几个方面…

BOSS 直聘:日增10亿数据的历史库,如何通过OceanBase节省70%存储成本?

BOSS 直聘是在全球范围内首创互联网“直聘”模式的在线招聘产品&#xff0c;目前已经成为了中国最大的招聘平台。本文谈到的 BOSS 直聘的业务场景主要是通过数据库对招聘过程中的聊天记录信息进行存储&#xff0c;数据量极大&#xff0c;且每天都有 5 亿到 10 亿的增量数据。和…

蓝桥杯省赛无忧 编程13 肖恩的投球游戏

#include <iostream> #include <vector> using namespace std; int main() {int n, q;cin >> n >> q;vector<int> a(n 1);vector<int> diff(n 2, 0); // 初始化差分数组// 读取初始球数&#xff0c;构建差分数组for (int i 1; i < …

JVM-字节码应用

一、字节码的应用远超你的想象 二、ASM介绍与读取字节码实战 用CoreAPI解析和TreeAPI都能做字节码解析&#xff0c;区别&#xff0c;TreeAPI必须读取完整字节码信息&#xff0c;才能做解析。 下面代码&#xff0c;使用CoreAPI做解析&#xff1a; package asm;public class MyM…

牛客NC16640纪念品分组(C++)

题目链接 实现方法 本题为简单的贪心问题。 将所有纪念品价值排序&#xff1b;使用双指针判断最大和最小的价值之和是否小于上限&#xff0c;若小于上限则取两个&#xff0c;否则取价值较大的一个物品&#xff1b;记录未分组的纪念品数量&#xff0c;若数量为0则跳出循环&…

《PCI Express体系结构导读》随记 —— 第I篇 第3章 PCI总线的数据交换(5)

接前一篇文章&#xff1a;《PCI Express体系结构导读》随记 —— 第I篇 第3章 PCI总线的数据交换&#xff08;4&#xff09; 3.2 PCI设备的数据传递 PCI设备的数据传递使用地址译码方式&#xff0c;当一个存储器读写总线事务到达PCI总线时&#xff0c;在这条总线上的所有PCI设…

Java入门高频考查基础知识7-深入挖掘Java集合框架的奇幻世界2(39题2.8万字参考答案)

Java 集合是 Java 编程中至关重要的组成部分&#xff0c;它为开发者提供了丰富、灵活、高效的数据结构和算法。无论是初学者还是有经验的开发者&#xff0c;在使用 Java 进行编程时都会频繁地接触到集合框架。这篇文章将深入探讨 Java 集合的重要性&#xff0c;以及为什么它对于…

写静态页面——魅族声学_前端页面练习

1、效果: 1、html代码: <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>魅族声学</titl…