JVM篇:垃圾回收

如何判断对象可以被回收

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对象引用,计数器加1B b = new B(a);//被对象b的属性a引用,计数器再加1a.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 -->bytefor (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.hqwc.cn/news/433620.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot中阿里云OSS的使用

目录 1 登录/注册阿里云并进入控制台 2 进入OSS控制台 3 创建bucket 4 查看bucket 5 获取AccessKey 6 查看帮助文档 7 添加Maven依赖 8 获取示例代码并改造成工具类 9 测试 1 登录/注册阿里云并进入控制台 2 进入OSS控制台 3 创建bucket 4 查看bucket 5 获取AccessKe…

【Spring】Spring简介、IOC、DI

目录 Spring简介 Spring Framework五大功能模块 IOC容器 IOC思想 IOC容器在Spring中的实现 基于XML管理bean 配置bean 获取bean 依赖注入之setter注入 依赖注入之构造器注入 特殊值处理 字面量赋值 null值 xml实体 CDATA节 为类类型属性赋值 为数组类型属性赋值 为集合类型属性…

Ubuntu20.04添加桌面启动、侧边栏启动和终端启动

桌面启动 新建XX.desktop文件 在桌面新建一个XX.desktop文件&#xff0c;以QtCreator为例。 &#xff08;注意这里不能使用sudo&#xff0c;因为这样会把文件的权限归为root&#xff0c;导致后续设置可执行程序不方便&#xff09; gedit qtcreator.desktop在XX.desktop文件中…

Java面向对象三大特征之继承(一)

在之前的文章我们从建立一个类开始&#xff0c;讲述了创建对象的有关知识&#xff0c;&#xff08;点此进入了解《Java——从建立一个类开始》&#xff09; 以及关于Java面向对象第一个特征封装&#xff08;点此了解 《封装》 ) 在这篇文章中&#xff0c;我们讲述关于继承的相关…

博途S7-1200PLC轴控指令轴暂停和急停处理(SCL代码)

S7-1200PLC位置控制相关功能块,请查看下面链接文章: https://rxxw-control.blog.csdn.net/article/details/135768878https://rxxw-control.blog.csdn.net/article/details/135768878S7-1200PLC位置控制功能块 https://rxxw-control.blog.csdn.net/article/details/1352993…

Docker 容器内运行 mysqldump 命令来导出 MySQL 数据库,自动化备份

备份容器数据库命令&#xff1a; docker exec 容器名称或ID mysqldump -u用户名 -p密码 数据库名称 > 导出文件.sql请替换以下占位符&#xff1a; 容器名称或ID&#xff1a;您的 MySQL 容器的名称或ID。用户名&#xff1a;您的 MySQL 用户名。密码&#xff1a;您的 MySQL …

懂醒酒才能让葡萄酒得到升华

懂醒酒才能让葡萄酒得到升华 一、什么是醒酒器&#xff1f; 醒酒器&#xff0c;亦作醒酒瓶、醒酒壶&#xff0c;是一种饮用新发酵葡萄酒时所用的器皿&#xff0c;作用是让酒与空气接触&#xff0c;让酒的香气充分挥发&#xff0c;并让酒里的沉淀物隔开。 醒酒器的形状标志一般…

用C语言实现贪吃蛇游戏!!!(破万字)

前言 大家好呀&#xff0c;我是Humble&#xff0c;不知不觉在CSND分享自己学过的C语言知识已经有三个多月了&#xff0c;从开始的C语言常见语法概念说到C语言的数据结构今天用C语言实现贪吃蛇已经有30余篇博客的内容&#xff0c;也希望这些内容可以帮助到各位正在阅读的小伙伴…

[docker] 关于docker的面试题

docker命名空间&#xff1f; docker与虚拟机的区别&#xff1f; 容器虚拟机所有容器共享宿主机的内核每个虚拟机都有独立的操作系统和内核通过namespace实现资源隔离&#xff0c;通过cgroup实现限制资源的最大使用量完全隔离。每个虚拟机都有独立的硬件资源秒级启动速度分钟级…

RabbitMQ 笔记二

1.Spring 整合RabbitMQ 生产者消费者 创建生产者工程添加依赖配置整合编写代码发送消息 创建消费者工程添加依赖配置整合编写消息监听器 2.创建工程RabbitMQ Producers spring-rabbitmq-producers <?xml version"1.0" encoding"UTF-8"?> <pr…

国考省考行测:判断推理,形式逻辑,充分条件,必要条件,用箭头,找真假,找不定

国考省考行测&#xff1a;判断推理 2022找工作是学历、能力和运气的超强结合体! 公务员特招重点就是专业技能&#xff0c;附带行测和申论&#xff0c;而常规国考省考最重要的还是申论和行测&#xff0c;所以大家认真准备吧&#xff0c;我讲一起屡屡申论和行测的重要知识点 遇到…

已经安装了CUDA,但是cmd执行nvcc -V报错:nvcc不是内部或外部命令,也不是可运行的程序或批处理文件

请注意&#xff0c;查看版本的指令是nvcc --version或nvcc -V&#xff0c;注意区分大小写 如果还是不能输出版本信息&#xff0c;那这个原因可能是由于没有在系统环境变量里添加CUDA。 先来看看CUDA是否安装成功&#xff1a; 在CUDA的安装路径下找到bandwidthTest.exe 和 devi…