JVM学习.02 内存分配和回收策略

1、前言

《JVM学习.01 内存模型》篇讲述了JVM的内存布局,其中每个区域是作用,以及创建实例对象的时候内存区域的工作流程。上文还讲到了关于对象存货后,会被回收清理的过程。今天这里就着重讲一下对象实例是如何被清理回收的,以及清理回收的几种算法。

2、再谈引用

JDK1.2版本之后,对引用的概念进行了扩充,分为强引用,软引用,弱引用,虚引用。这4种引用关系强度依次减弱。

2.1、Strongly Reference 强引用

强应用是最传统的”引用“定义。这种引用关系,无论任何情况(包括OOM异常),只要强引用关系还存在,GC就不会回收掉被引用对象

声明方式:

Object object = new Object();

2.2、Soft Reference 软引用

一种相对强引用弱化了一些的引用。比如高速缓存就可以用到软引用。当内存足够时就保留,不够时就回收。其中:

  • 当系统内存充足的时候,不会被回收;

  • 当系统内存不足的时,会将这些对象列进回收范围之中进行第二次回收,如果还是内存不足,才会抛出内存溢出异常。

声明方式:

 SoftReference softReference = new SoftReference<>(obj);

2.3、Weak Reference 弱引用

弱引用的强度比软引用更弱一些。被弱引用关联的对象,生命周期只能到下一次GC。当GC开始工作,无论当前的内存是否够用,都会会受到被弱引用关联的对象。

声明方式:

WeakReference weakReference = new  WeakReference<>(obj);

2.4、Phantom Reference 虚引用

虚引用是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对该对象的生存时间构成影响,也无法通过虚引用来取得一个对象实例。虚引用的作用主要是用来跟踪对象被垃圾回收的状态。

设值虚引用关联的唯一目的,就是在这个对象被回收的时候收到一个系统通知,或是后续添加进一步的操作处理。

声明方式:

PhantomReference phantomReference = new PhantomReference<>(obj, rq);

2.5、各引用小结

  • 强引用:对象不会被回收,出现OOM

  • 软引用:内存不够时才回收(二次清理)

  • 弱引用:只要GC就回收

  • 虚引用:用于检测对象的GC状态

3、如何判断对象“存活”

3.1、引用计数算法

在JVM中专门开辟一块额外的内存空间,专门用来对实例引用进行技数。一个对象如果在JVM中有被别人引用(关联或持有),则计数器+1;反之,则-1。任何时刻只要计数器为0的对象(没有任何指针对其引用),那么他就是不是存活,需要被清理。

这种的技数方式虽然原理简单,效率也很高,且有不错的案例使用。但是依然存在弊端。

看一段代码:

public class GcReferenceCount {
    public void testGC(){
        GcObject gcObj1 = new GcObject(); 
        GcObject gcObj2 = new GcObject(); 
        gcObj1.gcObj = gcObj2;
        gcObj2.gcObj = gcObj1;
        gcObj1 = null;
        gcObj2 = null;
        
        // 假设这里发生了gc
        System.gc();
    }
}

class GcObject {
    GcObject gcObj;
}

上述代码,gcObj1和gcObj2互为引用。就算当gcObj1 = null;gcObj = null;那么计数器永远不可能为0,意味着永远不可能被回收。

3.2、可达性分析算法

通过一系列被称为“GC Roots”的根对象作为起始节点集,从这些节点开始根据引用关系向下搜索,搜索过程所走的路径称为“引用链”。如果某个对象到GC Roots间都没有任何的引用链关联,或者说到GC Roots对象不可达的,则证明此对象是内存垃圾。

通过这种方式可以规避引用计数算法存在的相互指向的问题。也是目前GC中默认的分析标记算法。

网上借来的图:

3.3、并发的可达性分析

这里的并发指的是用户线程和GC线程同时工作。

3.2中提到的可达性分析算法用来断定对象是否存活。理论上要求标记的全过程都基于一个保障一致性的快照中才能进行(假设一边在进行链路搜索,一边用户线程又在更改对象引用指向,那么起初搜索过的路径就会存在歧义)。且往往需要标记的对象又是大多数,这时候随着堆变大而等比例的增加STW(停顿)时间,那么也将直接影响整个系统。

为了解决或降低用户线程的停顿,即要搞为什么必须要在一个能保证一致性的快照中才能进行。引入了”三色标记“算法作为工具来辅助推导。这里将对象按照”是否访问过“分成三种颜色:

  • 白色:该对象没有被GC访问过。

  • 黑色:该对象被GC访问过,他是安全存活的,且这个对象所有引用都被扫描过。

  • 灰色:该对象被GC访问过,但这个对象至少存在一个引用还没被扫描过。

关于并发可达性分析算法,可能存在两个问题:

1、原本消亡的对象被错误标记为存活,这个是可以容忍的。只不过产生了一点浮动垃圾而已,等待下次回收就可以了。

2、原本存活的对象被错误标记为消亡,这个可能会导致系统的致命错误。

关于并发出现”对象消失“问题示意图:

同时满足两个条件时,就会出现”对象消失“的问题:

1、赋值器插入一条或多条从黑色对象到白色对象的新引用;

2、赋值器删除了全部从灰色对象到该白色对象的直接或间接引用;

解决方式:

1、增量更新。破坏第一个条件。当黑色对象插入新的指向白色对象的引用时,把这个新的引用记录下来,等并发标记结束之后,再扫描一次这个记录。比如用一个队列记录下来。可以理解为,黑色对象一旦新插入白色对象的引用之后,它就变回灰色对象了。

2、原始快照。破坏第二个条件。当灰色对象要删除指向白色对象的引用时,就把这个要删除的引用记录下来,等并发标记结束之后,再以这个记录里的灰色对象为根,重新扫描一次。

4、内存回收策略

4.1、标记 - 清除算法

标记:标记处所有需要回收的对象(也可以反过来,标记存活的对象)。

清除:在标记完成后,统一回收所有被标记的对象(如果标记的是需要被回收的对象的话,否则反之)。

网上借的图:

主要缺点:

1、执行效率不稳定。如果Java堆中包含大量对象,且其中大部分是需要被回收的。必须进行大量的标记动作,导致执行效率会随着对象数量增加而降低;

2、空间碎片化。标记,清除后会产生大量不连续的内存碎片。空间碎片太多会导致后面大对象分配时无法找到足够的连续空间。

4.2、标记 - 复制算法

将内存分为大小相等的两块空间,每次只使用其中一块。

标记:标记处所有需要回收的对象(也可以反过来,标记存活的对象)。

复制:当其中一块的内存不足时,将存活的对象复制到另一块内存中。然后把这块的对象清理。

网上借的图:

主要缺点:

1、空间利用率低。以空间换时间的做法,造成空间浪费;其间始终有一块内存没有被使用。

2、效率问题。如果对象有大量都是存活的,那么复制的对象很多,效率自然也会低下。

主要优点:

适合大量对象都是短生命周期的。一次性收集后存活对象很少的情况。同时也避免了空间碎片的问题。

4.3、标记 - 整理算法

结合了标记清除和标记复制的优缺点。

标记:标记处所有需要回收的对象(也可以反过来,标记存活的对象)。

整理:当被标记对象需要被清理时,对存活的对象不进行复制,而是统一向一端移动,然后清理掉端边界外部的内存空间。

网上借的图:

主要缺点:

1、效率问题。每次存活对象的移动,都带来大量的内存重新寻址的计算量, 执行效率较低。甚至低于复制算法。

主要优点:

不会造成空间碎片和空间浪费问题。

4.4、分代收集原则

到目前为止,大多数的回收器都遵循分代垃圾收集原则。

新生代:以标记复制算法居多。大部分对象生命周期较短,采用复制算法可以避免一定的空间碎片问题,且效率比较高

老年代:标记清除或标记整理算法。因为对象的存活时间比较长。

5、小结

到这里,讲述了JVM中的内存回收,以及引用如何被垃圾收集器回收的一些算法。对JVM的内存使用更加了解。其实JVM相关内容看过很多次,但是从来没有过系统性的整理,大部分都停留在脑子中。第一次尝试整理这些内容,一方面可以加深自己的印象,另一方面,通过搜索其他的参考资料,可以发现很多以前忽略的地方。或许这个就是写技术博客的魅力吧。虽然千篇一律,但都是自己手敲原创。respect!

参考资料:《深入理解Java虚拟机》第三版

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

5.方法(最全C#方法攻略)

目录 5.1 方法的结构 5.2 方法体内部的代码执行 5.3.1 类型推断和Var关键字 5.3.2 嵌套块中的本地变量 5.4 本地常量 5.5 控制流 5.6 方法调用 5.7 返回值 5.8 返回语句和void 方法 5.9 参数 5.9.1 形参 5.9.2 实参 位置参数示例 5.10 值参数 5.11 引用参数 5.12…

【vm虚拟机】vmware虚拟机下载安装

vmware虚拟机下载安装&#x1f6a9; vmware虚拟机下载&#x1f6a9; 安装虚拟机程序&#x1f6a9; 创建一个CentOS虚拟机&#x1f6a9; 异常情况&#x1f6a9; vmware虚拟机下载 vmware官网下载地址 &#x1f6a9; 安装虚拟机程序 双击安装包exe程序&#xff0c;无脑下一步即…

来到CSDN的一些感想

之所以会写下今天这篇博客&#xff0c;是因为心中实在是有很多话想说&#xff01;&#xff01;&#xff01; 认识我的人应该都知道&#xff0c;我是才来CSDN不久的&#xff0c;也可以很清楚地看见我的码龄&#xff0c;直到今天&#xff1a;清清楚楚地写着&#xff1a;134天&…

完美日记母公司再度携手中国妇基会,以“创美人生”助力女性成长

撰稿 | 多客 来源 | 贝多财经 当春时节&#xff0c;梦想花开。和煦的三月暖阳&#xff0c;唤醒的不止是满城春意&#xff0c;更有逸仙电商“创美人生”公益项目播撒的一份希望。 3月8日“国际妇女节”当日&#xff0c;为积极响应我国促进共同富裕的政策倡导&#xff0c;助力相…

C语言--自定义类型详解

目录结构体结构体的声明特殊的声明结构的自引用typedef的使用结构体变量的定义和初始化结构体的内存对齐为什么存在内存对齐&#xff1f;修改默认对齐数结构体传参位段位段的内存分配位段的跨平台问题枚举联合联合类型的定义联合在内存中开辟空间联合大小的计算结构体 结构体的…

Linux之磁盘分区、挂载

文章目录一、Linux分区●原理介绍●硬盘说明查看所有设备挂载情况挂载的经典案例二、磁盘情况查询基本语法应用实例磁盘情况-工作实用指令一、Linux分区 ●原理介绍 Linux来说无论有几个分区&#xff0c;分给哪一目录使用&#xff0c;它归根结底就只有一个根目录&#xff0c;…

可编程线性直流电源的特性有哪些?

可编程线性直流电源是一种高性能、高精度的电源设备&#xff0c;其主要特性包括以下几点&#xff1a;1、高稳定性&#xff1a;可编程线性直流电源具有极高的输出稳定性&#xff0c;能够保证输出电压、电流和功率的精度和稳定性。通常来说&#xff0c;稳定性能够达到0.01%或更高…

Linux的诞生过程

个人简介&#xff1a;云计算网络运维专业人员&#xff0c;了解运维知识&#xff0c;掌握TCP/IP协议&#xff0c;每天分享网络运维知识与技能。座右铭&#xff1a;海不辞水&#xff0c;故能成其大&#xff1b;山不辞石&#xff0c;故能成其高。个人主页&#xff1a;小李会科技的…

Android Lancet Aop 字节编码修复7.1系统Toast问题(WindowManager$BadTokenException)

近期在Bugly上出现7.1以下设备上出现大量BadTokenException&#xff1a; android.view.WindowManager$BadTokenExceptionUnable to add window -- token android.os.BinderProxy6c0415d is not valid; is your activity running?报错堆栈&#xff0c;如下所示&#xff1a; …

数据分析师CDA认证 Level Ⅰ笔记

**黑色字体部分为考纲&#xff0c;蓝色字体部分为笔记&#xff0c;仅供参考 PART 1 数据分析概念与职业操守 1、数据分析概念、方法论、角色 【领会】 数据分析基本概念&#xff08;数据分析、数据挖掘、大数据&#xff09; 数据分析目的及其意义 数据分析方法与流程 数据分析的…

【网络安全工程师】从零基础到进阶,看这一篇就够了

学前感言 1.这是一条需要坚持的道路&#xff0c;如果你只有三分钟的热情那么可以放弃往下看了。 2.多练多想&#xff0c;不要离开了教程什么都不会&#xff0c;最好看完教程自己独立完成技术方面的开发。 3.有问题多google,baidu…我们往往都遇不到好心的大神&#xff0c;谁…

【Leetcode】队列实现栈和栈实现队列

目录 一.【Leetcode225】队列实现栈 1.链接 2.题目再现 3.解法 二.【Leetcode232】栈实现队列 1.链接 2.题目再现 3.解法 一.【Leetcode225】队列实现栈 1.链接 队列实现栈 2.题目再现 3.解法 这道题给了我们两个队列&#xff0c;要求去实现栈&#xff1b; 首先&…

8大核心语句,带你深入python

人生苦短 我用python 又来给大家整点好东西啦~ 咱就直接开练噜&#xff01;内含大量代码配合讲解 python 安装包资料:点击此处跳转文末名片获取 1. for - else 什么&#xff1f;不是 if 和 else 才是原配吗&#xff1f; No&#xff0c;你可能不知道&#xff0c; else 是个…

Cache的地址结构,tag到底与Cache什么关系,Cache容量与总容量,Cache行长,Cache字地址?

目录.Cache映射的问题一.Cache的三种映射重点&#xff1a;那么我说1.直接映射2.全相联映射3.组相联映射4.总结三种映射二.Cache的三个字眼(例题)1.Cache字地址多少位&#xff08;字地址即按字编址&#xff09;2.Cache容量与总容量3.Cache行长一.Cache的三种映射 重点&#xff…

C++ 类与对象

结构体与类&#xff1a;在C语言中结构体可以存储一些不同类型的数据&#xff0c;这个功能就很强大了&#xff0c;但是这些数据都是不安全的我们可以在主函数中随意修改它&#xff0c;在C中的类可以很好的解决这个问题。类就相当于C语言中的结构体一样&#xff0c;C结构体&#…

GC 垃圾回收机制

文章目录JVM 的内存模型对象存活&#xff1f;引用计数算法可达性分析算法垃圾收集标记-清除算法标记-复制算法标记-整理算法垃圾收集器垃圾收集器发展Serial / Serial OldParallel Scavenge / Parallel OldParNew / CMSG1ZGC扩展JVM 的内存模型 Java 虚拟机&#xff08;Java V…

转速/线速度/角速度计算FC

工业应用中很多设备控制离不开转速、线速度的计算,这篇博客给大家汇总整理。张力控制的开环闭环方法中也离不开转速和线速度的计算,详细内容请参看下面的文章链接: PLC张力控制(开环闭环算法分析)_plc的收卷张力控制系统_RXXW_Dor的博客-CSDN博客里工业控制张力控制无处不…

按键修改阈值功能、报警功能、空气质量功能实现(STM32)

按键修改阈值功能 要使用按键&#xff0c;首先要定义按键。通过查阅资料&#xff0c;可知按键的引脚如图所示&#xff1a;按键1&#xff08;S1&#xff09;通过KEY0与PA0连接&#xff0c;按键2&#xff08;S2&#xff09;通过KEY1与PE2连接&#xff0c;按键3&#xff08;S3&…

收到6家大厂offer,我把问烂了的《Java八股文》打造成3个文档。共1700页!!

前言大家好&#xff0c;最近有不少小伙伴在后台留言&#xff0c;近期的面试越来越难了&#xff0c;要背的八股文越来越多了&#xff0c;考察得越来越细&#xff0c;越来越底层&#xff0c;明摆着就是想让我们徒手造航母嘛&#xff01;实在是太为难我们这些程序员了。这不&#…

【LINUX】初识文件系统

文章目录一、前言二、回顾C语言文件操作三、初识系统调用openreadwriteclose四、文件系统初识五、结语一、前言 二、回顾C语言文件操作 int main() {FILE* fp fopen("log.txt", "w");if (fp NULL){perror("fopen");}int cnt 0;fputs("…