上一篇文章我们学到了对象在内存中是如何存储的已经是如何被访问的,这篇文章将介绍当内存空间不够时,虚拟机将怎样判定对象可不可以被回收已经哪些地方会发生回收。

  垃圾回收主要(不是全部)发生在堆内存中,当一个对象没有存在的必要的时候,占着内存明显不行,所以Java内置的GC会对没有必要存在的内存区域进行回收,那么,如何判断一个对象已经没有使用价值了,或者说,已死了呢?

  首先,最直观的方式是在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就+1,当引用值失效时,再-1,任何时刻计数器值为0的对象就是不可能再被使用的,客观的说,这种方式实现简单,判定效率高,在大部分情况下是一个不错的算法,但是它最大的问题在于不能解决对象之间相互引用的问题,例如:objA.instance = objB; objB.instance = objA; 这样两个对象之间互相引用,引用计数器的值都不为0,但是很明显除此之外没有别的地方需要这两个对象,此时使用这种回收机制就显得无能为力。此时,另一种算法:可达性分析,在主流的商用程序语言中,都使用到了这种算法,这种算法的思路是:从一系列定义为GC-Root的对象作为起点,从这些节点开始向下搜索,搜索所走过的路径称为“引用链”,当一个对象到GC-Root对象没有任何引用链相连时,即无法到达时,认为该对象已经没用了,这种算法很好的解决了上述中提到的对象之间互相引用的问题。

  在Java中,可以作为GC-Roots的对象有以下几种:

  虚拟机栈中引用的对象

  方法区中静态属性引用的对象

  方法区中常量引用的对象

  本地方法栈JNI中引用的对象

  从上文可知,无论是引用计数器和可达性分析(引用链)都与引用这个概念相关,那么什么是引用呢?在JDK1.2以前,引用的定义很直接:如果reference类型数据中存储的是另一块内存的起始地址,就称这块内存存在着一个引用。这种定义很纯粹,但是有些狭隘,它无法描述那些“食之无味,弃之可惜”的对象,我们希望能描述这样一类对象:当内存空间还足够时,可以保留在内存中,如果内存空间在垃圾回收后还是很紧张,则可以抛弃这些对象,很多系统的缓存功能都符合这样的应用场景。于是,在JDK1.2之后,Java定义了四种引用类型:

  1.强引用:在代码中普遍存在,类似"Object a = new Object()"就是强引用,只要强引用存在,垃圾回收器就不会回收被引用的内存。

  2.软引用:Soft Reference,用来描述一些有用但不是必须的对象,当将要发生内存溢出时,会将这些对象列为回收范围进行第二次回收,如果这次回收后还没有足够的可用内存,才会抛出内存溢出。

  3.弱引用:描述的对象与软引用相似,但是比软引用更弱一些,只能生存到下次GC之前,当虚拟机进行内存回收时,无论是否存在软引用,都会对该对象进行回收,在JDK1.2之后,提供了WeakReference来表示弱引用。

  4.虚引用:Phantom Reference,也被称为幽灵引用或幻影引用,是最弱的一种引用关系,他的存在不会对对象的生存时间构成任何影响,也无法通通过虚引用获取一个对象实例,引入它只是为了在被系统回收时得到一个通知。

  tips:当一个对象被可达性算法和引用计数法都判定为“死亡”时,还会进行一次筛选,如果这个类覆盖了Object类的finalize()方法(对于一个对象,这个方法只会调用一次),且可以调用时,会先调用该方法,执行完该方法后如果还是没有满足可存活的条件,才会将其回收,这是文章作者特意提到的,对象可以在该方法中进行“自救”,例如:

  

  将自己this赋值给一个引用,但是只能自救一次,因为该方法只会被调用一次。

  但是作者建议不要使用这种方式拯救对象,因为调用不确定性大,开销大,用finalize()可以做到的,用try-finally可以做的更好,只须了解有这个方法存在即可。

  方法区也是会发生内存回收的

  很多人会认为方法区中不会进行内存回收的操作,虽然在方法区进行内存回收的效率比较低(正常在堆中进行一次GC大约可以释放70%~95%的内存空间),但是还是会执行内存回收,回收的对系那个有两种,一是废弃的常量,二是无用的类。

  1.例如一个常量“abc”已经进入了常量池,但是当前系统没有一个String对象是指向该常量,如果这时候发生了内存回收,并且有必要的话,就会将该常量回收。

  2.判断一个常量是否无用简单,但是判断一个类是否无用比较复杂,需要满足多个条件,首先,该类的所有实例均以被回收,也就是Java堆中不存在该类的实例;其次,该类的ClassLoader已经被回收;并且,该类的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。虚拟机可以对上述类进行回收,但是仅仅是可以,而不是像对象一样,不使用了就必然会回收。

  下一章会介绍虚拟机主要使用的几种垃圾回收算法。

  

深入理解JVM(四) -- 垃圾内存回收的判定方法和内容的更多相关文章

  1. 图解JVM垃圾内存回收算法

    图解JVM垃圾内存回收算法 这篇文章主要介绍了图解JVM垃圾内存回收算法,由于年轻代堆空间的垃圾回收会很频繁,因此其垃圾回收算法会更加重视回收效率,下面博主和大家来一起学习一下吧 前言 首先,我们要讲 ...

  2. 推荐收藏系列:一文理解JVM虚拟机(内存、垃圾回收、性能优化)解决面试中遇到问题(图解版)

    欢迎一起学习 <提升能力,涨薪可待篇> <面试知识,工作可待篇 > <实战演练,拒绝996篇 > 欢迎关注我博客 也欢迎关注公 众 号[Ccww笔记],原创技术文章 ...

  3. JVM(四)内存回收(二)

    在上一节中"JVM(三)内存回收(一)"我讲到了垃圾回收的几种算法,算是解决了之前提到的3个问题中的最后一个. 关于内存回收,还应该了解常用的内存回收器(GC Collector) ...

  4. 深入理解JVM一垃圾回收算法

    我们都知道java语言与C语言最大的区别就是内存自动回收,那么JVM是怎么控制内存回收的,这篇文章将介绍JVM垃圾回收的几种算法,从而了解内存回收的基本原理. 一.stop the world 在介绍 ...

  5. JVM(四) 垃圾回收

    1. 堆内存结构 Java堆从GC的角度可以细分为:新生代(Eden区.From Survivor区和To Survivor区)和老年代. 1.1 新生代 新生代是用来存放新生的对象.一般占据堆的1/ ...

  6. 深入理解JVM——关于垃圾回收

    关于垃圾回收 仿佛来自上海居委会大妈的灵魂拷问:“你是什么垃圾?” 不 今天我们要说的是JVM的垃圾回收 假如我是一个“人”类的“对象”,也和人的生命一样必有一死,可是“我真的还想再活500年~~”, ...

  7. 深入理解JVM(五) -- 垃圾回收算法

    上篇文章我们了解到哪些内存区域和哪些对象可以被回收,这篇文章我们就来了解一下具体的垃圾回收算法的思路,不讨论具体的实现. 一 最基础算法 标记-清除(Mark-Swap) 为什么说他是最基础的算法,因 ...

  8. 理解JVM之Java内存区域

    Java虚拟机运行时数据区分为以下几个部分: 方法区.虚拟机栈.本地方法栈.堆.程序计数器.如下图所示: 一.程序计数器 程序计数器可看作当前线程所执行的字节码行号指示器,字节码解释器工作时就是通过改 ...

  9. 深入理解JVM - 1 - Java内存区域划分

    作者:梦工厂链接:https://www.jianshu.com/p/7ebbe102c1ae来源:简书简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处. Java与C++之间有一堵 ...

随机推荐

  1. 计算电脑所能表示的最大最小值(c++)

    C++当中获得现在计算机上所能表示的各种类型(比如int,long int,short int,double,float等)最大最小有两种方法,一种是使用c++预先定义的宏,对于有些编译器可能需要包含 ...

  2. 浅析package.json中的devdependencies 和 dependencies

    2.devDependencies (1)内容:是一个对象,配置模块依赖的模块列表,key是模块名称,value是版本范围(2)作用:该模块中所列举的插件属于开发环境的依赖(比如:测试或者文档框架等) ...

  3. Servlet使用反射机制

    传统servlet存在的问题 每一个不同的请求都要写Servlet,导致整个项目servlet过多,不易维护 解决方案 同一个模块只写一个Servlet,然后每一个请求传一个参数,后台根据参数取调用不 ...

  4. 解决:E: Could not get lock /var/lib/dpkg/lock

    问题: ubuntu16 执行 sudo apt install aria2 命令时,提示如下错误 E: Could not get lock /var/lib/dpkg/lock - open (1 ...

  5. Cannot capture jmeter traffic in fiddler

    Cannot capture jmeter traffic in fiddler First, change Fiddler's port back to 8888 as it was origina ...

  6. [转]Windows内存堆内容整理总结

    在系统安全研究中,堆,是一个极其重要的内存区域以及研究的热点.堆,区别于栈区.全局数据区以及代码区,它的主要作用是允许程序在运行时动态地申请某个大小的内存空间.本文将从宏观到微观,简单梳理总结一下Wi ...

  7. 解决Bootstrap标签页(Tab)插件切换echarts不显示问题

    1.参考连接:https://blog.csdn.net/qq_24313955/article/details/78363981 问题描述:在echarts跟bootstrap选项卡整合的时候,默认 ...

  8. XmlIgnore的解释和使用

    XmlIgnore是一个自定义属性,用来指明在序列化时是否序列化一个属性.如下面的例子: public class Group { public string GroupName; [XmlIgnor ...

  9. sqlite数据库使用具体案例以及mysqlite.db数据库

    本文操作是测试数据库的其中一张表,其中包括清空sqlite数据库MyGroup表中的数据,清空sqlite数据库sqlite_sequence表中的自增变量,sqlite数据库MyGroup直接插入数 ...

  10. 文件数据库sqlite3 C++ 线程安全和并发

    转载:https://www.cnblogs.com/feng9exe/p/10682567.html(线程安全和并发) 转载:https://juejin.im/post/5b7d8522e51d4 ...