JVM(三)对象的生死判定和算法详解

好的文章是能把各个知识点,通过逻辑关系串连起来,让人豁然开朗的同时又记忆深刻。

导读:对象除了生死之外,还有其他状态吗?对象真正的死亡,难道只经历一次简单的判定?如何在垂死的边缘“拯救”一个将死对象?判断对象的生死存活都有那些算法?本文带你一起找到这些答案。

在正式开始之前,我们先来了解一下垃圾回收。

GC介绍

**GC:**Garbage Collection,中文翻译为垃圾回收。

GC的历史

GC有着很长的历史了,最初的GC算法发布于1960年(已经快有60年的历史了),Lisp之父John McCarthy发布的,他是一名非常有名的黑客,也是人工智能之父,同时也是GC之父。

为什么要学习GC?

1、排查内存溢出和内存泄露的问题。

2、系统调优,处理更高的并发瓶颈。

GC的作用

1、找到内存空间的垃圾。

2、回收垃圾。

对象生死判断算法

垃圾回收的第一步就是判断对象是否存活,只有“死去”的对象,才会被垃圾回收器所收回。

引用计数器算法

引用计算器判断对象是否存活的算法是这样的:给每一个对象设置一个引用计数器,每当有一个地方引用这个对象的时候,计数器就加1,与之相反,每当引用失效的时候就减1。

**优点:**实现简单、性能高。

**缺点:**增减处理频繁消耗cpu计算、计数器占用很多位浪费空间、最重要的缺点是无法解决循环引用的问题。

因为引用计数器算法很难解决循环引用的问题,所以主流的Java虚拟机都没有使用引用计数器算法来管理内存。

来看一段循环引用的代码:

public class ReferenceDemo {public Object instance = null;private static final int _1Mb = 1024 * 1024;private byte[] bigSize = new byte[10 * _1Mb]; // 申请内存public static void main(String[] args) {System.out.println(String.format("开始:%d M",Runtime.getRuntime().freeMemory() / (1024 * 1024)));ReferenceDemo referenceDemo = new ReferenceDemo();ReferenceDemo referenceDemo2 = new ReferenceDemo();referenceDemo.instance = referenceDemo2;referenceDemo2.instance = referenceDemo;System.out.println(String.format("运行:%d M",Runtime.getRuntime().freeMemory() / (1024 * 1024)));referenceDemo = null;referenceDemo2 = null;System.gc(); // 手动触发垃圾回收System.out.println(String.format("结束:%d M",Runtime.getRuntime().freeMemory() / (1024 * 1024)));}
}

运行的结果:

开始:117 M

运行中:96 M

结束:119 M

从结果可以看出,虚拟机并没有因为相互引用就不回收它们,也侧面说明了虚拟机并不是使用引用计数器实现的。

可达性分析算法

在主流的语言的主流实现中,比如Java、C#、甚至是古老的Lisp都是使用的可达性分析算法来判断对象是否存活的。

这个算法的核心思路就是通过一些列的“GC Roots”对象作为起始点,从这些对象开始往下搜索,搜索所经过的路径称之为“引用链”。

当一个对象到GC Roots没有任何引用链相连的时候,证明此对象是可以被回收的。如下图所示:

可达性分析算法

在Java中,可作为GC Roots对象的列表:

  • Java虚拟机栈中的引用对象。
  • 本地方法栈中JNI(既一般说的Native方法)引用的对象。
  • 方法区中类静态常量的引用对象。
  • 方法区中常量的引用对象。

对象生死与引用的关系

从上面的两种算法来看,不管是引用计数法还是可达性分析算法都与对象的“引用”有关,这说明:对象的引用决定了对象的生死。那对象的引用都有那些呢?

在JDK1.2之前,引用的定义很传统:如果reference类型的数据中存储的数值代表的是另一块内存的起始地址,就称这块内存代表着一块引用。

这样的定义很纯粹,但是也很狭隘,这种情况下一个对象要么被引用,要么没引用,对于介于两者之间的对象显得无能为力。

JDK1.2之后对引用进行了扩充,将引用分为:

  • 强引用(Strong Reference)

  • 软引用(Soft Reference)

  • 弱引用(Weak Reference)

  • 虚引用(Phantom Reference)

这也就是文章开头第一个问题的答案,对象不是非生即死的,当空间还足够时,还可以保留这些对象,如果空间不足时,再抛弃这些对象。很多缓存功能的实现也符合这样的场景。

强引用、软引用、弱引用、虚引用,这4种引用的强度是依次递减的。

**强引用:**在代码中普遍存在的,类似“Object obj = new Object()”这类引用,只要强引用还在,垃圾收集器永远不会回收掉被引用的对象。

**软引用:**是一种相对强引用弱化一些的引用,可以让对象豁免一些垃圾收集,只有当jvm认为内存不足时,才会去试图回收软引用指向的对象。jvm会确保在抛出OutOfMemoryError之前,清理软引用指向的对象。

**弱引用:**非必需对象,但它的强度比软引用更弱,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。

**虚引用:**也称为幽灵引用或幻影引用,是最弱的一种引用关系,无法通过虚引用来获取一个对象实例,为对象设置虚引用的目的只有一个,就是当着个对象被收集器回收时收到一条系统通知。

死亡标记与拯救

在可达性算法中不可达的对象,并不是“非死不可”的,要真正宣告一个对象死亡,至少要经历两次标记的过程。

如果对象在进行可达性分析之后,没有与GC Roots相连接的引用链,它会被第一次标记,并进行筛选,筛选的条件是此对象是否有必要执行finalize()方法。

执行finalize()方法的两个条件:

1、重写了finalize()方法。

2、finalize()方法之前没被调用过,因为对象的finalize()方法只能被执行一次。

如果满足以上两个条件,这个对象将会放置在F-Queue的队列之中,并在稍后由一个虚拟机自建的、低优先级Finalizer线程来执行它。

对象的“自我拯救”

finalize()方法是对象脱离死亡命运最后的机会,如果对象在finalize()方法中重新与引用链上的任何一个对象建立关联即可,比如把自己(this关键字)赋值给某个类变量或对象的成员变量。

来看具体的实现代码:

public class FinalizeDemo {public static FinalizeDemo Hook = null;@Overrideprotected void finalize() throws Throwable {super.finalize();System.out.println("执行finalize方法");FinalizeDemo.Hook = this;}public static void main(String[] args) throws InterruptedException {Hook = new FinalizeDemo();// 第一次拯救Hook = null;System.gc();Thread.sleep(500); // 等待finalize执行if (Hook != null) {System.out.println("我还活着");} else {System.out.println("我已经死了");}// 第二次,代码完全一样Hook = null;System.gc();Thread.sleep(500); // 等待finalize执行if (Hook != null) {System.out.println("我还活着");} else {System.out.println("我已经死了");}}
}

执行的结果:

执行finalize方法

我还活着

我已经死了

从结果可以看出,任何对象的finalize()方法都只会被系统调用一次。

不建议使用finalize()方法来拯救对象,原因如下:

1、对象的finalize()只能执行一次。

2、它的运行代价高昂。

3、不确定性大。

4、无法保证各个对象的调用顺序。

参考

《深入理解Java虚拟机》

《垃圾回收的算法与实现》

※ 为写好一篇技术文章,背后是读了两本书的“艰辛”。写作不易,请多支持!!!

最后

关注公众号,发送“gc”关键字,领取《垃圾回收的算法与实现》学习资料。

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

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

相关文章

【STM32】修改芯片型号后报 Error 的解决方案

原文:https://blog.csdn.net/xiuhua_wu/article/details/85237418 背景 前几天有个新需求,使用 STM32 的标准库(STD)做个产品的例程。之前已经做了个 HAL 的,但人家客户不干,非要 STD 的。拖了一周&#xf…

Python手写神经网络实现3层感知机

一、BP神经网络结构模型 BP算法的基本思想是,学习过程由信号的正向传播和误差的反向传播俩个过程组成,输入从输入层输入,经隐层处理以后,传向输出层。如果输出层的实际输出和期望输出不符合,就进入误差的反向传…

JVM(四)垃圾回收的实现算法和执行细节

全文共 1890 个字,读完大约需要 6 分钟。 上一篇我们讲了垃圾标记的一些实现细节和经典算法,而本文将系统的讲解一下垃圾回收的经典算法,和Hotspot虚拟机执行垃圾回收的一些实现细节,比如安全点和安全区域等。 因为各个平台的虚拟…

python-cx_oracle报错“DatabaseError: DPI-1047: 64-bit Oracle Client library cannot be loaded: “

问题的主要原因是python-cx_oracle加载的是32位的instantclient-basic,我们需要让他读到64位的。 弄清版本,最重要!!! 首先安装配置时,必须把握一个点,就是版本一致!包括&#xff1…

JVM(五)垃圾回收器的前世今生

全文共 2195 个字,读完大约需要 8 分钟。 如果垃圾回收的算法属于内存回收的方法论的话,那本文讨论的垃圾回收器就属于内存回收的具体实现。 因为不同的厂商(IBM、Oracle),实现的垃圾回收器各不相同,而本文…

【SlowFast复现】SlowFast Networks for Video Recognition复现代码 使用自己的视频进行demo检测

目录 一,准备 1.1代码1.2 环境准备1.3 搭建镜像1.4 配置slowfast环境1.5 ava.json1.6 SLOWFAST_32x2_R101_50_50.yaml1.7 SLOWFAST_32x2_R101_50_50 .pkl二,代码运行三 错误解决复现过程视频:B站复现视频复现结果 一,准备 1.1代…

MySQL学生向笔记以及使用过程问题记录(内含8.0.34安装教程

MySQL 只会写代码 基本码农 要学好数据库,操作系统,数据结构与算法 不错的程序员 离散数学、数字电路、体系结构、编译原理。实战经验, 高级程序员 去IOE:去掉IBM的小型机、Oracle数据库、EMC存储设备,代之以自己在开源…

程序员专属精品简历合集—面试必备

听说你最近打算换工作?听说你和好工作之间,只差一个漂亮的简历模板?人们常说“金三银四”,一年之际在于春。不管你是主动离职,还是被动“被离职”(稳住,我们能赢!)&#…

ubuntu20.10下wine安装微信

1.unbuntu20.04下安装wine sudo apt-get install wine2.微信官网http://short.weixin.qq.com/下载window系统微信软件 3.在wine中安装微信 在WeChatSetup.exe文件中打开终端输入: wine WeChatSetup.exe

【faster rcnn 实现via的自动框人】使用detectron2中faster rcnn 算法生成人的坐标,将坐标导入via(VGG Image Annotator)中,实现自动框选出人的区域

前言 B站讲解视频 我的研究生毕业论文方向就是时空行为检测,所以,slowfast和ava是我重点搞的,我的博客主页也有很多这些相关内容。 终于,到了标注数据这一块了,为了更简单的标注数据,我要做的这部分的数据…

程序员精美简历Top榜—面试必备

听说你最近打算换工作?听说你和好工作之间,只差一个漂亮的简历模板? 不管是主动离职,还是“被离职”(稳住,我们能赢!),趁着大好时光和对新年的憧憬,再找一个…

失败创业者的告白:初创团队应有一位绝对领导者

做了两年的项目失败了,我们的项目做的是数码3C的优惠信息,我是一个80后,小硕一枚;我们的1号创始人是90后,有激情、有梦想;在十八岁那年带上他的梦想千里北上,找我们的开复老师指点一二;但那时,开复老师正为…

【ffmpeg裁剪视频faster rcnn自动检测 via】全自动实现ffmpeg将视频切割为图片帧,再使用faster rcnn将图片中的人检测出来,最后将检测结果转化为via可识别的csv格式

目录 前言一,ffmpeg 自动裁剪 1.1 目录结构1.2 cutVideoToImage.sh1.2 myVideo1.3 myVideo15mins1.5 myFrames1.6 运行1.7 查看结果二,detectron2中的faster rcnn检测 2.1 img2.2 myvia.py2.3 运行2.4 结果展示三,via标注 3.1 csv文件修改&am…

ubuntu20.10(Linux)在wine下用pyinstaller打包python程序在window系统运行 交叉编译

1.安装wine 在终端中输入: sudo apt-get install wine2.安装pip 在https://pypi.org/project/pip/#files下载pip-21.0.tar.gz,在压缩包上右键提取到此处,打开解压的文件夹pip-21.0 在该文件夹中打开终端安装pip-21.0.tar.gz: wi…

JVM(六)为什么新生代有两个Survivor分区?

本文会使用排除法的手段,来讲解新生代的区域划分,从而让读者能够更清晰的理解分代回收器的原理,在开始之前我们先来整体认识一下分代收集器。 分代收集器会把内存空间分为:老生代和新生代两个区域,而新生代又会分为&a…

【slowfast复现 训练】训练过程 制作ava数据集 复现 SlowFast Networks for Video Recognition 训练 train

目录 前言一,ava相关文件准备 1.1 空间准备(500G)1.2 整体ava文件结构1.3 frames文件1.4 frame_lists 文件1.5 annotations 文件二,预训练模型三,配置文件 3.1 创建新的yaml文件3.2 yaml文件解释四,训练前言…

Qt6 在线安装图文步骤

说明: Qt 自从5.15版本开始,对非商业版本,也就是开源版本,不提供已经制作好的离线exe安装包,自这个版本开始你只有两种选择: 1、编译源码 例如qt-everywhere-src-5.15.2.zip 编译步骤繁琐,需严…

【slowfast 减少ava数据集】将ava数据集缩小到2个,对数据集做训练,然后进行检测,为训练自己的数据集做准备

目录 前言一,数据文件结构 1.1 myava1.2 annotations1.3 annotations文件处理1.4 frame_lists1.5 frames二,预训练模型四,配置文件 4.1 创建新的yaml文件4.2 yaml文件解释五,训练六,结果查看前言 b站讲解 ava的数据集…

JAVA反射系列之Field,java.lang.reflect.Field使用获取方法。

2019独角兽企业重金招聘Python工程师标准>>> 首先必须明一点 Field类主要是用来辅助获取和操作类的属性的! 1.怎么通过反射获取类的属性 先来看JDK提供的方法有如下几种: a)Class.getDeclaredField(String name); 返回一个 Field 对象&#x…

Python3自带HTTP文件传输服务(局域网文件共享)

一行命令搭建一个基于python3的http文件传输服务 WIN10系统下,打开打算要分享传输的文件夹(文件夹内存放分享的文件,建议以压缩包形式存放),shift鼠标右键,出现如下图,选择“在此处打开Powershe…