简述JVM垃圾回收机制

简述JVM垃圾回收机制1.Java中的四种引用类型在Java中,对于引用最基本的解释就是:如果reference类型的数据中存储的数值代表的是另外一块内存的起始地址,就称这块内存代表着一个引用(有点指针的意味)。后来Java还将引用划分为了4种,根据被GC回收的时机可以分为:强引用(StrongReference)、软引用(SoftReference)、弱引用(WeakReference)、虚引用(Phantor

大家好,又见面了,我是你们的朋友全栈君。

1. Java中的四种引用类型

在Java中,对于引用最基本的解释就是:如果reference类型的数据中存储的数值代表的是另外一块内存的起始地址,就称这块内存代表着一个引用(有点指针的意味)。后来Java还将引用划分为了4种,根据被GC回收的时机可以分为:强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantorm Reference)。这4种引用的强度依次渐弱。

1.1 强引用

Object o = new  Object();

这里的o就是一个强引用,也是我们用得最多的引用,在实例化类的时候经常会用到。遇到这类引用,GC(垃圾回收器)是绝对不会回收它的。当遇到内存不足的情况,JVM会抛出OOM异常。所以,在不使用这类对象的时候要注意释放它,以便让系统回收。

1.2 软引用

SoftReference<String> s = new SoftReference<>(new String("Hello"));
System.out.println(w.get());

此时会输出
Hello

这里的s就是一个软引用,它是用来描述一些有用但非必需的对象,当系统内存出现不足的时候,会立即把这些对象进行回收。
软引用可以用来实现内存的缓存,如图片缓存。

1.3 弱引用

WeakReference<String> w = new WeakReference<>(new String("Hello"));
System.out.println(w.get());
System.gc();
System.out.println(w.get());

此时会输出:

Hello
null

弱引用的强度比软引用低一些,一旦GC开始执行,便会对这类对象进行回收。也就是说无论内存足够与否,弱引用对象都只能生存到下一次GC之前。

1.4 虚引用

ReferenceQueue<String> queue = new ReferenceQueue<>();
PhantomReference<String> pr = new PhantomReference<>(new String("Hello"), queue);
System.out.println(pr.get());

此时会输出:

null

虚引用也称为幽灵引用,它的强度是最低的,一个对象是否有虚引用的存在,完全不会对它的生存时间构成影响,当然也无法通过虚引用获取一个类的实例,GC任何时候都有可能会回收此对象。它唯一的作用就是就是用于追踪,让我们能够在这个对象被回收的时候收到一个通知。

2.如何判断对象需要被回收

2.1 引用计数法

比较尴尬的是以前刚上Java课的时候,我去问老师JVM是如何确定需要被回收的对象的时候,老师就给我讲了引用计数法,但后来随着学习的深入,我发现主流的JVM都没有采用这一方法,原因是它有很大的一个缺陷,就是难以解决对象之间相互引用问题。
所谓引用计数法,就是在对象内部会有一个引用计数器,一旦某个地方引用它时,计数器就加1,表面上看这是一个效率非常高的方式,但是如果遇到如下情形时,采用了这种方式判断对象是否存活的GC就难以发挥作用了。

public static void main(String[] args) {
    A a = new A();
    B b = new B();
    //a,b循环引用对方
    a.intance = b; //b的引用计数加1
    b.intance = a; //a的引用计数加1
    //虽然a和b都被设置成null了,但它们的引用计数仍然是1,不会被GC回收 
    a = null;
    b = null;
    System.gc();
}

2.2 可达性分析法

相比之下,可达性分析算法就要靠谱得多。所谓可达性分析就是通过一系列被称为“GC Roots”的点作为起始点,从这些节点开始向下搜索,搜索的路径称为引用链,当一个对象到GC Roots不可达的时候,则证明此对象是可回收的。
GC Root
上图中,从GC Root到ObjectC、ObjectE、ObjectF都不可达,所以这三个对象就是会被GC回收的对象。

3.垃圾回收算法

3.1 标记清除法

标记清楚法有两个过程,一是标记过程,即判定需要回收的对象的过程,通过可达性分析便可分析出来。二是清除阶段,这个阶段就是对标记了的对象进行回收。
标记清除法
这种回收算法非常简单粗暴,也很好理解,但是暴露出来问题还是比较大的,需要优化的地方还有很多。一是标记和清除的效率都不是很高。二是执行完GC后会造成大量的内存碎片,如果以后分配大内存对象的的时候无法找到足够大的连续内存,就会频繁触发GC。

3.2 复制算法

为了解决内存碎片问题,复制算法出现了,它将可用内存平均划分为两块,每次只使用其中一块。当这一块用完了,就将还存活的对象复制到另一块,然后再把已使用的内存空间一次清理掉。这样每次GC的时候都直接回收半个内存空间的大小,不必考虑碎片问题。但这种方法带来的代价也不小,牺牲了一半的存储空间。
复制算法

3.3 标记整理法

如果在对象存活率较高的时候采用复制算法的话,复制的操作就会有很多,对程序的运行效率也会产生一定的影响,此时就应该考虑标记整理法。标记整理法的执行过成分为3步:1.用与“标记清除法”一样的操作标记存活的对象;2.将被标记的对象全部移动到内存的某一端;3.清除边界以外部分的内存。
标记整理法

3.4 分代回收法

在分代回收算法中,会根据对象的存活周期,将内存划分为几块,一般是新生代(Young Generation)、老年代(Old Generation)、永久代(Permanent Generation)。这样就可以根据不同内存区域的特点执行采用不同的回收算法。像新生代这种经常有大批对象死去的区域,就适合用复制算法。而对于老年代这种对象生存周期较长和永久代这种内存存活率较高,又没有其他担保空间的地方就用标记清除法或标记整理法就行了。

3.4.1 新生代

对于新生代,往往会分为三个区:一个Eden区和两个Survivor区(图中是S0和S1,其实这两个区域没有任何区别,只是取个名字方便叙述)。这样划分后结合复制算法对垃圾进行回收效果和效率都会比较高。具体的工作流程如下:
新生代的分区
程序中大部分对象都是在新生代的Eden区域中,当GC来临时,会遵循如下机制:
1.当Eden区域触发GC的时候,将Eden中还存活的对象复制到S0中,再清空Eden区。
2.然后当S0触发GC时候,将S0和Eden中还存活的对象复制到S1中,再清空S0和Eden区。
3.当S1也触发GC的时候,就将此区域内的从S0复制过来并且GC后还存活的对象复制到老年代,再清空S1。
值得注意的是:GC发生的时机是根据剩余内存的大小和具体的算法来决定的,不一定是当内存存满了才触发一次GC。并且这里的GC指的是Minor GC,也就是用一些快速回收算法实现的高效率GC,这种方式只会对新生代进行回收。

3.4.2 老年代

老年代中则存放着新生代复制过来的对象,这些一般都是经历过多次GC存活下来的对象,所以这里的对象生存的时间都比较长。当老年代存满了时候,会调用Full GC也就是对整个堆(包括新生代、老年代、永久代)进行回收,这样的回收更彻底,但是时间也消耗更多。

3.4.3 永久代

永久代一般用来存放一些静态变量,静态方法等,这里一般发生GC的概率比较小,但是一旦存满,也是会直接触发Full GC的。

4.内存分配策略

JVM的一大优势是解决的内存方面的两个重要的问题:自动给对象分配内存 和 自动回收分配给对象的问题。一般来说,分配对象需要符合以下原则:

4.1 对象优先在Eden分配

当Eden区没有足够空间进行分配时,虚拟机将发起一次Minor GC。

4.2 大对象直接进入老年代

所谓的大对象是指:需要大量连续内存空间的Java对象。最典型的大对象就是那种很长的字符串以及数组。这样可以有效避免大对象在Eden和Survivor之间的频繁复制操作带来的性能开销。(当然我们在写代码的时候也要注意避免写出生命周期太短的大对象,因为这并不符合老年代的特点)

4.3 长期存活的对象将会进入老年代

根据老年代的特点,每当对象熬过了一次Minor GC的时候,它的age就将会增加1,当年龄增长到一定的值时(默认为15岁),它就将进入老年代。

4.4 动态对象年龄判定

对于“老年对象”的判定并不一定是要根据”默认的年龄要求(15岁)“,如果在Survivor空间中相同年龄所有对象的大小的总和大于Survivor空间的一半的时候,年龄大于或等于该年龄的对象将会直接进入老年代。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/132168.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • C++ XML 库 TinyXML2 的基本使用

    C++ XML 库 TinyXML2 的基本使用0.前言TinyXML-2是一个简单,小型,高效的C++XML解析器,可以轻松集成到其他程序中,直接引用源文件的话只需要包含两个文件(h和cpp,此外还有个测试文件里面带有demo)。TinyXML-2解析XML文档,并以此为基础构建可读取,修改和保存的文档对象模型(DOM)。文档说,在解释XML时仅使用UTF-8,假定所有XML为UTF-8(看了下使用MSVC编译器时生成的XML文件文本编码使用的本地编码)。该库还支持打印到文件或内存,使用XMLPr

  • kindeditor php配置,KindEditor-编辑器配置参数属性 | 小灰灰博客

    kindeditor php配置,KindEditor-编辑器配置参数属性 | 小灰灰博客在《开发php接入富文本编辑器KindEditor》中提到了KindEditor编辑器的使用方法,通过使用KE.show(config)方法即可将编辑器添加到文档中。·无论使用哪种方式使用KindEditor编辑器,都必须进行参数配置;·可以在KE.show()和KE.init()方法中传递配置参数config;·配置参数config是一个对象,一般使用字面量对象;·其中,id参数是唯一必须进行配…

    2022年10月12日
  • 应用层,驱动层,硬件层_windows组件向导在哪里

    应用层,驱动层,硬件层_windows组件向导在哪里驱动层与应用层通信是通过DeviceIoControl,首先驱动层要实现:pDriverObject->DriverUnload=MyDriverUnload;pDriverObject->MajorFunction[IRP_MJ_CREATE]=MyCreate;pDriverObject->MajorFunction[IRP_MJ_CLOSE]=MyClose;

  • 宝塔服务器搭建网站教程_宝塔linux面板漏洞

    宝塔服务器搭建网站教程_宝塔linux面板漏洞腾讯云免费SSL证书是腾讯云为用户提供的一款免费一年使用的SSL证书,用起来方便、快捷。同时搭配现在很热门的建站神器:宝塔面板,即使小白也能在很短时间内搞定网站域名“小绿锁”。今天老魏详细讲解如何申请腾讯云免费SSL证书,并部署到宝塔面板中。一、注册帐号在腾讯云申请证书首先需要注册腾讯云账号并且完成实名认证。新用户请点我直达腾讯云官网,从右上角的【免费注册】,进入注册页面。注册后要先完成实名认证,…

  • react 路由守卫

    react 路由守卫通过HOC的方式实现路由守卫

  • fread函数_sumifs函数详解

    fread函数_sumifs函数详解“fread”以二进制形式,从文件读出数据。语法1:[a,count]=fread(fid,size,precision)语法2:[a,count]=fread(fid,size,precision,

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号