前言

这一节我们来简单的介绍垃圾收集器,并学习垃圾标记的算法:引用计数算法和根搜索算法,为了更好的理解根搜索算法,会在文章的最后介绍Java对象在虚拟机中的生命周期。

1.垃圾收集器概述

  垃圾收集器(Garbage Collection),通常被称作GC。提到GC,很多人认为它是伴随Java而出现的,其实GC出现的时间要比Java早太多了,它是1960诞生于MIT的Lisp。 
GC主要做了两个工作,一个是内存的划分和分配,一个是对垃圾进行回收。关于内存的划分和分配,目前Java虚拟机内存的划分是依赖于GC的的设计的,比如现在GC都是采用了分代收集算法来回收垃圾,Java堆作为GC主要管理的区域,被细分为新生代和老年代,再细致一点新生代又可以划分为Eden空间、From Survivor空间、To Survivor空间等,这样进行划分是为了更快的进行内存分配和回收。空间划分后,GC就可以为新对象分配内存空间。 
关于对垃圾进行回收,被引用的对象是存活的对象,而不被引用的对象是死亡的对象也就是垃圾,GC要区分出存活的对象和死亡的对象,也就是垃圾标记,并对垃圾进行回收。接下来我们先来介绍垃圾标记算法。

2.垃圾标记算法

在对垃圾进行回收前,GC要先标记出垃圾,那么如何标记呢,目前有两种垃圾标记算法,分别是引用计数算法和根搜索算法,这两个算法都和引用有些关联,因此讲垃圾标记算法前,我们先回顾下引用的知识。

引用

在JDK1.2之后,Java将引用分为强引用、软引用、弱引用和虚引用。

  • 强引用:当我们new一个对象时就是创建了一个具有强引用的对象,如果一个对象具有强引用,垃圾收集器就绝不会回收它。Java虚拟机宁愿抛出OutOfMemoryError异常,使程序异常终止,也不会回收具有强引用的对象来解决内存不足的问题。
  • 软引用:如果一个对象只具有软引用,当内存不够时,会回收这些对象的内存,回收后如果还是没有足够的内存,就会抛出OutOfMemoryError异常。Java提供了SoftReference类来实现软引用。
  • 弱引用:弱引用比起软引用具有更短的生命周期,垃圾收集器一旦发现了只具有弱引用的对象,不管当前内存是否足够,都会回收它的内存。Java提供了WeakReference类来实现弱引用。
  • 虚引用:虚引用并不会决定对象的生命周期,如果一个对象仅持有虚引用,这就和没有任何引用一样,在任何时候都可能被垃圾收集器回收。一个只具有虚引用的对象,被垃圾收集器回收时会收到一个系统通知,这也是虚引用的主要作用。Java提供了PhantomReference类来实现虚引用。

引用计数算法

引用计数算法的基本思想就是每个对象都有一个引用计数器,当对象在某处被引用的时候,它的引用计数器就加1,引用失效时就减1。当引用计数器中的值变为0,则该对象就不能被使用成了垃圾。 
目前主流的Java虚拟机没有选择引用计数算法来为垃圾标记,主要原因是引用计数算法没有解决对象之间相互循环引用的问题。 
举个例子,下面代码的注释1和注释2处,d1和d2相互引用,除此之外这两个对象无任何其他引用,实际上这两个对象已经死亡,应该作为垃圾被回收,但是由于这两个对象互相引用,引用计数就不会为0,垃圾收集器就无法回收它们。

class _2MB_Data {public Object instance = null;private byte[] data = new byte[2 * 1024 * 1024];//用来占内存,测试垃圾回收
}public class ReferenceGC {public static void main(String[] args) {_2MB_Data d1 = new _2MB_Data();_2MB_Data d2 = new _2MB_Data();d1.instance = d2;//1d2.instance = d1;//2d1 = null;d2 = null;System.gc();}
}

View Code

如果你使用Android Studio,就在Edit Configurations中的VM options加入如下语句来输出详细的GC日志:

-XX:+PrintGCDetails

 1 运行程序,GC日志为:
 2 [GC (System.gc()) [PSYoungGen: 8028K->832K(76288K)] 8028K->840K(251392K), 0.0078334 secs] [Times: user=0.06 sys=0.00, real=0.01 secs]
 3 [Full GC (System.gc()) [PSYoungGen: 832K->0K(76288K)] [ParOldGen: 8K->603K(175104K)] 840K->603K(251392K), [Metaspace: 3015K->3015K(1056768K)], 0.0045844 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
 4 Heap
 5 PSYoungGen total 76288K, used 1966K [0x000000076af80000, 0x0000000770480000, 0x00000007c0000000)
 6 eden space 65536K, 3% used [0x000000076af80000,0x000000076b16bac0,0x000000076ef80000)
 7 from space 10752K, 0% used [0x000000076ef80000,0x000000076ef80000,0x000000076fa00000)
 8 to space 10752K, 0% used [0x000000076fa00000,0x000000076fa00000,0x0000000770480000)
 9 ParOldGen total 175104K, used 603K [0x00000006c0e00000, 0x00000006cb900000, 0x000000076af80000)
10 object space 175104K, 0% used [0x00000006c0e00000,0x00000006c0e96d10,0x00000006cb900000)
11 Metaspace used 3046K, capacity 4496K, committed 4864K, reserved 1056768K
12 class space used 334K, capacity 388K, committed 512K, reserved 1048576K
13
14 查看此GC日志前我们先来简单了解下各参数的含义,[GC (System.gc()和[Full GC (System.gc()说明了这次垃圾收集的停顿类型,而不是来区分新生代GC和老年代GC的。 [Full GC (System.gc() 说明这次GC发生了STW,STW也就是Stop the World机制,意思是说在执行垃圾收集算法时,只有GC线程在运行,其他的线程则会全部暂停,等待GC线程执行完毕后才能再次运行。
15 PSYoungGen代表新生代,ParOldGen代表老年代,Metaspace代表元空间(JDK 8中用来替代永久代PermGen)。
16 我们来看日志的[GC (System.gc()),内存变化为:8028K->840K(251392K),8028K代表回收前的内存大小,840K代表回收后的内存大小,251392K代表内存总大小。因此可以得知内存回收大小为(8028-840)K。这就说明JDK8的HotSpot虚拟机并没有采用引用计数算法来标记内存,它对上述代码中的两个死亡对象的引用进行了回收。

View Code

根搜索算法

这个算法的基本思想就是选定一些对象作为GC Roots,并组成根对象集合,然后从这些作为GC Roots的对象作为起始点,向下进行搜索,如果目标对象到GC Roots是连接着的,我们则称该目标对象是可达的,如果目标对象不可达则说明目标对象是可以被回收的对象,如下图所示。 

从上图看以看出,Obj5、Obj6和Obj7都是不可达的对象,其中Obj5和Obj6虽然互相引用,但是因为他们到GC Roots是不可达的所以它们仍旧会判定为可回收的对象,这样根搜索算法就解决了引用计数算法无法解决的问题:已经死亡的对象因为相互引用而不能被回收。 
在Java中,可以作为GC Roots的对象主要有以下几种:

  • Java栈中的引用的对象。
  • 本地方法栈中JNI引用的对象。
  • 方法区中运行时常量池引用的对象。
  • 方法区中静态属性引用的对象。
  • 运行中的线程
  • 由引导类加载器加载的对象
  • GC控制的对象

还有一个问题是被标记为不可达的对象会立即被垃圾收集器回收吗?要回答这个问题我们首先要了解Java对象在虚拟机中的生命周期。

3.Java对象在虚拟机中的生命周期

当Java对象被类加载器加载到虚拟机中后,Java对象在Java虚拟机中有7个阶段。 
1.创建阶段(Created) 
创建阶段的具体步骤为:

  • 为对象分配存储空间。
  • 构造对象。
  • 从超类到子类对static成员进行初始化。
  • 递归调用超类的构造方法。
  • 调用子类的构造方法。

2.应用阶段(In Use) 
当对象被创建,并分配给变量赋值,状态就切换到了应用阶段。 
这一阶段的对象至少要具有一个强引用,或者显式的使用软引用、弱引用或者虚引用。

3.不可见阶段(Invisible) 
程序中找不到对象的任何强引用,比如程序的执行已经超出了该对象的作用域。在不可见阶段,对象仍可能被特殊的强引用GC Roots持有着,比如对象被本地方法栈中JNI引用或是被运行中的线程引用等。

4.不可达阶段(Unreachable) 
程序中找不到对象的任何强引用,并且垃圾收集器发现对象不可达。

5.收集阶段(Collected) 
垃圾收集器已经发现对象不可达,并且垃圾收集器已经准备好要对该对象的内存空间重新进行分配时。这个时候如果该对象重写了finalize方法,则会调用该方法。

6.终结阶段(Finalized) 
当对象执行完finalize法后仍然处于不可达状态时,或者对象没有重写finalize方法,则该对象进入终结阶段,并等待垃圾收集器回收该对象空间。

7.对象空间重新分配阶段(Deallocated) 
当垃圾收集器对对象的内存空间进行回收或者再分配时,这个对象就会彻底消失。

好了,我们已经了解了Java对象在虚拟机中的生命周期,再来回想我方才说的问题:被标记为不可达的对象会立即被垃圾收集器回收吗?很显然是不会的,被标记为不可达的对象会进入收集阶段,这时会执行该对象重写的finalize方法,如果没有重写finalize方法或者finalize方法中没有重新与一个可达的对象进行关联才会进入终结阶段,并最终被回收。

JVM 深入笔记(3)垃圾标记算法 
GC roots 
Java GC - 监控回收行为与日志分析 
Java:对象的强、软、弱和虚引用 
JVM GC中Stop the world案例实战 
Java对象的生命周期

转载于:https://www.cnblogs.com/ganchuanpu/p/6217414.html

Java虚拟机(三)垃圾标记算法与Java对象的生命周期相关推荐

  1. 了解java虚拟机mdash;垃圾回收算法(5)

    引用计数器法(Reference Counting) 引用计数器的实现很简单,对于一个对象A,只要有任何一个对象引用了A,则A的引用计数器就加1,当引用失效时,引用计数器减1.只要对象A的引用计数器的 ...

  2. Java Web复习——task03:JSP 内置对象和生命周期

    文章目录 前言 一.JSP内置对象 1.1 HttpServletRequest类和Request对象 1.2 HttpServletResponse类和Response对象 1.3 JspWrite ...

  3. java虚拟机多久触发垃圾回收_每日一问:讲讲 Java 虚拟机的垃圾回收

    昨天我们用比较精简的文字讲了 Java 虚拟机结构,没看过的可以直接从这里查看: 每日一问:你了解 Java 虚拟机结构么? 今天我们必须来看看 Java 虚拟机的垃圾回收算法是怎样的.不过在开始之前 ...

  4. 学习笔记【Java 虚拟机②】垃圾回收

    若文章内容或图片失效,请留言反馈.部分素材来自网络,若不小心影响到您的利益,请联系博主删除. 总目录 学习笔记[Java 虚拟机①]内存结构 学习笔记[Java 虚拟机②]垃圾回收 学习笔记[Java ...

  5. 深入理解Java虚拟机——JVM垃圾回收机制和垃圾收集器详解

    一:概述 说起垃圾回收(Garbage Collection,GC),很多人就会自然而然地把它和Java联系起来.在Java中,程序员不需要去关心内存动态分配和垃圾回收的问题,顾名思义,垃圾回收就是释 ...

  6. 浅析Java虚拟机的垃圾回收机制(GC)

    目录 一.垃圾回收机制(Garbage Collection) 二.对象回收的时机 引用计数法 可达性分析算法 三.垃圾回收算法 标记-清除算法 标记-复制算法 标记-整理算法 新生代.老年代.永久代 ...

  7. Java虚拟机规范阅读(二)IEEE754简介以及Java虚拟机中的浮点算法

    什么是浮点数 在计算机系统的发展过程中,曾经提出过多种方法表达实数.典型的比如相对于浮点数的定点数(Fixed Point Number).在这种表达方式中,小数点固定的位于实数所有数字中间的某个位置 ...

  8. java虚拟机、垃圾回收、多线程

    虚拟机,我们都知道Java程序运行在虚拟机上,虚拟机又和操作系统打交道,最终通过二进制指令操纵电子电路运行.完成数据的读取,存储,运算和输出. 虚拟机在加载.class文件的时候,会在内存开辟一块区域 ...

  9. Java虚拟机之垃圾回收详解一

    Java虚拟机之垃圾回收详解一 Java技术和JVM(Java虚拟机) 一.Java技术概述: Java是一门编程语言,是一种计算平台,是SUN公司于1995年首次发布.它是Java程序的技术基础,这 ...

  10. java中的垃圾回收算法

    java中有四种垃圾回收算法,分别是: 标记清除法.标记整理法.复制算法.分代收集算法 1.标记清除法: 第一步:利用可达性去遍历内存,把存活对象和垃圾对象进行标记; 第二步:在遍历一遍,将所有标记的 ...

最新文章

  1. RecyclerView中adapter列表里有EditText输入框问题(使用不复用方法解决)
  2. mysql workbench ssh_通过MySQL Workbench进行SSH隧道
  3. php如何定义和使用常量,如何在PHP中定义和使用常量
  4. C++ STL : 模拟实现STL中的容器适配器stack和queue
  5. 蓝桥杯 2011年第二届C语言初赛试题(4)
  6. ubuntu 在vm中如何上网及注意问题
  7. 【BZOJ2751】【codevs1853】容易题,快速幂+逆元
  8. Android Studio 解决 Cannot resolve symbol xxx 添加依赖后出现飘红
  9. [转载] python判断是否为json_Python判断变量是否为Json格式的字符串示例
  10. 数据结构与算法系列----字典树
  11. 亲爱的,别把上帝缩小了 ---- 读书笔记4
  12. 【回归预测】基于matlab麻雀搜索算法优化CNN回归预测【含Matlab源码 282期】
  13. 传智播客-刘意-java深入浅出精华版学习笔记Day01
  14. css选择文件的图片大小,怎么用css设置图片大小
  15. 应用之星:H5制作教程-最好用的H5制作工具
  16. Echarts5.3.0-南丁格尔玫瑰图之半圆
  17. 如何把自己打造成技术圈的papi酱
  18. 【求职经历】四川移动暑期实习面试经历
  19. 前端实现 导出图片,导出PDF(截图原理)
  20. 谈谈你对Spring 的理解

热门文章

  1. 诗意的边缘(PHP顶级框架Zend Fr
  2. 12月22日 冬至 隋文帝赏柳上上签
  3. 一个简单的模拟键盘鼠标操作的类
  4. CSS3中很容易混淆的transform,translate,transition。如何去区分,以及综合写法。
  5. VMware Workstation虚拟机安装
  6. java中常见对象——StringBuffer
  7. 在mac上用parallels创建双windows虚拟机调试windows驱动
  8. 【转】项目代码风格要求
  9. Citrix XenServer
  10. ECharts之force力导向布局图——数据源说明及后端API约定