系统性学习JVM请点击JVM学习目录

为什么要垃圾回收

为什么要进行垃圾回收?做任何事之前我们都要搞清做这件事的原因。当我们在运行java项目时,如果项目比较大,它会实例化很多很多对象,多到我们的内存可能都装不下,导致内存溢出,从而项目没法继续跑下去,而在这些对象之中,有很多对象实际上我们只是前面用个几次,到后面我们就不用了,那么我们是不是就可以将这些不用的对象视为垃圾,给从内存中剔除出去,回收内存空间。这样就能大大的节省了我们的内存资源,把节省下的内存资源给新的对象使用,从而避免内存溢出。

垃圾回收发生在哪里

在前面jvm学习 java内存区域这篇博客中,我们介绍了java内存区域包含程序计数器,虚拟机栈,本地方法栈,方法区和堆这5个部分。程序计数器、虚拟机栈和本地方法栈都是线程私有的,随着线程的出生而出生,随着线程的死亡而死亡。它们是不存在垃圾回收的,更何况虚拟机栈时刻发生着入栈出栈的操作,基本很少会出现栈内存溢出的问题。所以这里我们的垃圾回收发生在堆和方法区这两个线程共享的区域。
值的一提的是,很多人都认为方法区不会被垃圾回收,其实不然,方法区也会被垃圾回收,其中的一些废弃的常量啊和不再使用的类型都可能被回收,不过它们被回收的收益不高,而且jvm规范提到过可以不实现方法区的垃圾回收,所以这里我们就不对方法区展开讲,把注意力集中到我们的堆上。
通过前面的学习我们知道,堆是用来存储我们的代码运行过程中实例化的对象和数组的,主要功能就是存储对象,所以我们回收堆内存也就是将一些我们不再使用的对象进行回收。那么什么对象可以认为是垃圾呢,谁是垃圾呢?

谁是垃圾

这里判断对象是否是垃圾的逻辑是对象是存活还是死亡。所谓死亡,就是没有变量该对引用象,则该对象就是死亡了。当没有变量引用该对象时,则意味着该对象无法再继续被用户使用。这里打个比方:变量与对象的关系就像是手里拿的线轴和天上飞的风筝的关系。线轴通过线“引用”风筝,那么用户就可以用线轴来操控风筝,而一旦对象死亡,没有变量引用该对象了,就是连接线轴和风筝的线断了,那么此时用户没法控制风筝,风筝就在天上飘着。此时,断了线风筝就成了垃圾。所以总结一句话:没有被变量引用的对象就是垃圾,需要被回收
知道了什么是垃圾之后,可能还是有些迷糊,能不能举个例子呢,这太抽象了,好的。那我们举两个例子:

        Person father = new Person("father");Person son = new Person("son");son.print();son = father;son.print();

这样一个例子,father引用的对象给了变量son,那么此时father和son都指向同一个对象,而son一开始引用的对象则成了断线的风筝,没有变量引用,此时它就是个垃圾,我们再也无法使用它了。
又比如,在我们的方法中,创建的局部变量引用的实例对象,他们随着栈帧入虚拟机栈而生,随着出栈而亡,那他们所引用的对象也就没有变量再引用了。
大致明白了对象的死亡后,问题又来了,怎么判断对象没有变量引用呢,你逻辑上说的好听,可jvm怎么实现呢?好的下面就来讲讲jvm如何判定对象是否死亡。
主要有两个方法:引用计数算法和可达性分析算法。现在基本用的都是可达性分析算法。

引用计数算法

讲起来也是很简单,我们为每个对象分配一个引用计数器,初始值为0,增加一个变量引用时加1,减少一个变量引用时减1,当引用计数器变为0时,则认定该对象死亡。
该方法会有一个问题,就是当两个对象互相引用时,就麻烦了。如下:


public class A {public B b;
}
public class B {public A a;
}
public class demo02 {public static void main(String[] args) {A a = new A();B b = new B();a.b = b;b.a = a;a = null;b = null;}
}

如上代码所示,a中的成员变量引用b,b中的成员变量引用a,然后我们令a,b为null,此时a,b互相引用,他们的引用计数器都是1,jvm不能视其为垃圾,而实际上我们已经无法通过变量(句柄)来使用对象了,他们确实是垃圾。这就发生了矛盾,所以在java中我们不用引用计数算法。那么,我们用什么呢?可达性分析算法。

可达性分析算法

如其名,可达性。一听就感觉与图论有点关系(话说图论是真的恶心啊,主要还是太枯燥懒得听)。可以发现,我们的对象实例其实很多都是有关系的,互相之间存在引用。这里可达性分析算法也是依赖这一点,选取一些对象作为根节点,从根节点开始,以引用关系往下遍历,如果能够遍历,则说明存活,如果遍历不到,说明该节点(对象)死亡(这里的引用关系我们只关注从根节点开始往下,往上的不考虑)。
拿上面讲的例子再讲一遍,假如我们以a引用的对象为根节点,从它开始往下遍历,那么马上就能访问到该对象中成员变量所引用的b变量引用的对象。那么此时a引用的对象和b引用的对象都可达,便都是存活的。

如上图所示(其实是棵树哈哈哈,引用对象就是子节点),对象1,2,3可达,则存活,5,6,7不可达,则死亡,为垃圾。
那么问题来了,哪些对象可以作为我们的根节点(GC Roots)?如下:

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象。(引用栈帧中的本地变量表的所有对象,方法使用的参数,局部变量(我们上面提到过),临时变量等)
  • 方法区中静态属性引用的对象(引用方法区该静态属性的所有对象)
  • 方法区中常量引用的对象(引用方法区中常量的所有对象)
  • 本地方法栈中(Native方法)引用的对象(引用Native方法的所有对象)
  • java虚拟机内部的引用,如基本数据类型所对应的class对象,一些常驻的异常对象,还有类加载器。
  • 所有被同步锁锁住的对象。(被锁住的对象当然不能被判定为死亡啦)
  • 反映java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等。

总结一下,按照如上规则选取GC Roots对象,然后以之为根节点,向下遍历,可达便存活,不可达便死亡。

(关于并发可达性分析,我也有写博客,可以去看一下)

四大引用

  1. 强引用
    以前我们使用的大部分引用实际上都是强引用,这是使用最普遍的引用。如果一个对象具有强引用,那就类似于必不可少的生活用品,垃圾回收器绝不会回收它。当内存空
    间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。
  2. 软引用(SoftReference)
    如果一个对象只具有软引用,那就类似于可有可物的生活用品。如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。
    软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,JAVA虚拟机就会把这个软引用加入到与之关联的引用队列中。
    当内存足够大时可以把数组存入软引用,取数据时就可从内存里取数据,提高运行效率
    软引用在实际中有重要的应用,例如浏览器的后退按钮。
    按后退时,这个后退时显示的网页内容是重新进行请求还是从缓存中取出呢?这就要看具体的实现策略了。
  3. 弱引用(WeakReference)
    如果一个对象只具有弱引用,那就类似于可有可物的生活用品。弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它
    所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,
    因此不一定会很快发现那些只具有弱引用的对象。
    弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。
  4. 虚引用(PhantomReference)
    “虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。虚引用主要用来跟踪对象被垃圾回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

(本节上述四大引用内容摘自https://blog.csdn.net/junjunba2689/article/details/80601729)

总结一下,对于一些不是经常用的对象,我们对它们的态度是,能用最好,能用的话省的我再实例化,节省资源,响应更快,但是呢,如果内存不够,就优先把这些对象从内存中释放,不能它们呢也不碍事,等我需要用的时候再实例化呗,不就是等一会儿呗,不影响。所以就出现了又这些个不同级别的引用。

参考资料

  • 《深入理解JVM》周志明

JVM学习 谁是垃圾?判断对象是否能被垃圾回收 可达性分析 四大引用相关推荐

  1. jvm学习第十、十一天、十二天—垃圾回收器1、垃圾回收的相关概述2、 垃圾回收相关算法3、 垃圾回收器

    标题:jvm学习第十.十一天.十二天-垃圾回收器 学习内容: 1.垃圾回收的相关概述 2. 垃圾回收相关算法 3. 垃圾回收器 内容详情: 1.垃圾回收的相关概述 什么是垃圾( Garbage)? 垃 ...

  2. JVM的GC如何判断对象是否死亡?

    因为热爱,所以坚持 文章下方有本文参考电子书和视频的下载地址哦~ 这节我们主要讲垃圾收集的一些基本概念,先了解垃圾收集是什么.然后触发条件是什么.最后虚拟机如何判断对象是否死亡. 一.前言   我们都 ...

  3. JVM学习笔记(三)------内存管理和垃圾回收

    JVM内存组成结构 JVM栈由堆.栈.本地方法栈.方法区等部分组成,结构图如下所示: 1)堆 所有通过new创建的对象的内存都在堆中分配,其大小可以通过-Xmx和-Xms来控制.堆被划分为新生代和旧生 ...

  4. JAVA垃圾回收-可达性分析算法

    在java中是通过引用来和对象进行关联的,也就是说如果要操作对象,必须通过引用来进行.那么很显然一个简单的办法就是通过引用计数来判断一个对象是否可以被回收.不失一般性,如果一个对象没有任何引用与之关联 ...

  5. JVM学习笔记(二):垃圾收集

    程序计数器. 虚拟机栈. 本地方法栈3个区域随线程而生,随线程而灭:栈中的栈帧随着方法的进入和退出而有条不紊地执行着出栈和入栈操作. 每一个栈帧中分配多少内存基本上是在类结构确定下来时就已知的,因此这 ...

  6. 判断是否存在此对象_JVM的垃圾回收机制,判断对象是否死亡

    这节我们主要讲垃圾收集的一些基本概念,先了解垃圾收集是什么.然后触发条件是什么.最后虚拟机如何判断对象是否死亡. 一.前言 我们都知道Java和C++有一个非常大的区别就是Java有自动的垃圾回收机制 ...

  7. 如何判断对象是否是垃圾

    前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到网站. 我们都Java会自动进行内存管理,JVM会进行垃圾回收,哪它是怎么判定哪些是"垃圾&quo ...

  8. java 确定对象的引用_JVM学习笔记之了解对象存活判断和4种引用【三】

    垃圾收集器在对堆进行回收前,第一件事情就是要确定这些对象之中哪些还"存活"着,哪些已经"死去" 一.对象存活判断 1.1 引用计数算法(Reference Co ...

  9. JVM判断对象是否存活

    引用计数法 可达性分析算法 引用计数法 给对象添加一个引用计数器,每当有一个地方引用,计数器就加1,当引用失效,计数器减1,计数器为0的对象没有被使用,Java中没有使用引用计数法,原因是引用计数法无 ...

最新文章

  1. Django 模板HTML转义和CSRF4.3
  2. TSNE-原理与实现
  3. QT 4.5 windows版本 安装问题 及 Junction 使用
  4. python selenium 元素定位_python3+selenium入门04-元素定位
  5. 【CyberSecurityLearning 18】ACL及实验演示
  6. idea包名呈现层级显示
  7. python列表索引超出范围 等于啥_python - IndexError:列表分配索引超出范围,Python
  8. Educational Codeforces Round 114总结
  9. mapperscan注解_SpringBoot 遗忘后的简单快速回忆之环境搭建与常见注解
  10. 随机数的生成 java
  11. Java生产环境下性能监控与调优详解 第6章 Nginx性能监控与调优
  12. Sklearn专题实战——针对Category特征进行分类
  13. 电商促销后台设计,写得太好了!
  14. redis3.2版本protected-mode参数
  15. xp计算机启动检测硬盘,WindowsXP系统,每次开机都自动检测硬盘处理办法
  16. [番外]:带你玩正则1--数据遍地是,看你取不取
  17. 安卓手机如何投屏到电视上_如何将手机投屏到电视上?原来这么简单好用
  18. Vue 2项目如何升级到Vue 3?
  19. 手淘流量是什么意思?手淘搜索流量怎么增加?
  20. duilib底层机制剖析:窗体类与窗体句柄的关联

热门文章

  1. nitro5 装w7_将Nitro PDF Reader与Windows 7集成
  2. 27岁女测试员,月入3万开宝马!IT测试行业真的唠?
  3. .NET基础笔记(C#)
  4. python-求1~100的素数
  5. [分享]牛牛图片查看器[仿QQ图片查看器]及大致原理说明
  6. 计算机局域网之组成结构
  7. java 图片上一张 下一张_java实现预览图片,点击实现下一张
  8. 更改docker存储路径
  9. C语言中的字符函数和字符串函数
  10. 预训练语言模型fine-tuning近期进展概述