先说一些题外话,Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区,这些区分为线程私有区和线程共享区

  1、线程私有区

    a、程序计数器

    记录正在执行的虚拟机字节码指令地址。此区域是是唯一一个在java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。

   b、Java虚拟机栈

    描述的是Java方法执行的内存模型,每个方法在执行的同时会创建一个栈帧

   c、本地方法栈

    它与虚拟机栈发挥的作用是类似的,它们之间的区别不过是虚拟机栈为虚拟机执行java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用的Native方法服务。

  2、线程共享区

   a、Java堆

    被所有线程共享的一块内存区域,也是Java虚拟机所管理的内存中最大的一块。

   b、方法区

    用于存储已被虚拟机加载的类信息、常量、静态变量、即时编辑器编译后的代码等数据,虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名Non-Heap(非堆)

  下面开始说正题

  目前虚拟机基本都是采用可达性算法,为什么不采用引用计数算法呢?下面就说说引用计数法是如何统计所有对象的引用计数的,再对比分析可达性算法是如何解决引用技术算法的不足。先简单说说这两个算法:

  1、引用计数法(reference-counting):每个对象都有一个引用计数器,当对象被引用一次,计数器就加1,当对象引用时效一次就减,当计数器为0,意味着对象是垃圾对象,可以被GC回收。

  2、可达性算法(GC Root Tracing):从GC Root作为起点开始搜索,那么整个连通图中对象都是活的,对于GC Root无法达到的对象便是垃圾对象,随时可被GC回收。

  采用引用计数算法的系统只需在每个实例对象创建之初,通过计数器来记录所有的引用次数即可。而可达性算法,则需要再次GC时,遍历整个GC根节点来判断是否回收。

  下面通过一段代码来对比说明:

  

 public class GcDemo {

    public static void main(String[] args) {
//分为6个步骤
GcObject obj1 = new GcObject(); //Step 1
GcObject obj2 = new GcObject(); //Step 2 obj1.instance = obj2; //Step 3
obj2.instance = obj1; //Step 4 obj1 = null; //Step 5
obj2 = null; //Step 6
}
} class GcObject{
public Object instance = null;
}

  1、引用计数算法

  如果采用的是引用计数算法:

  再回到前面代码GcDemo的main方法共分为6个步骤:

  • Step1:GcObject实例1的引用计数加1,实例1的引用计数=1;
  • Step2:GcObject实例2的引用计数加1,实例2的引用计数=1;
  • Step3:GcObject实例2的引用计数再加1,实例2的引用计数=2;
  • Step4:GcObject实例1的引用计数再加1,实例1的引用计数=2;

  执行到Step 4,则GcObject实例1和实例2的引用计数都等于2。

  接下来继续结果图:
  

  • Step5:栈帧中obj1不再指向Java堆,GcObject实例1的引用计数减1,结果为1;
  • Step6:栈帧中obj2不再指向Java堆,GcObject实例2的引用计数减1,结果为1。

  到此,发现GcObject实例1和实例2的计数引用都不为0,那么如果采用的引用计数算法的话,那么这两个实例所占的内存将得不到释放,这便产生了内存泄露。

  

  2、可达性算法

  这是目前主流的虚拟机都是采用GC Roots Tracing算法,比如Sun的Hotspot虚拟机便是采用该算法。 该算法的核心算法是从GC Roots对象作为起始点,利用数学中图论知识,图中可达对象便是存活对象,

  而不可达对象则是需要回收的垃圾内存。这里涉及两个概念,一是GC Roots,一是可达性。

  那么可以作为GC Roots的对象(见下图):

  • 虚拟机栈的栈帧的局部变量表所引用的对象;
  • 本地方法栈的JNI所引用的对象;
  • 方法区的静态变量和常量所引用的对象;

  关于可达性的对象,便是能与GC Roots构成连通图的对象,如下图:
  

  从上图,reference1、reference2、reference3都是GC Roots,可以看出:

  • reference1-> 对象实例1;
  • reference2-> 对象实例2;
  • reference3-> 对象实例4;
  • reference3-> 对象实例4 -> 对象实例6;

  可以得出对象实例1、2、4、6都具有GC Roots可达性,也就是存活对象,不能被GC回收的对象。
  而对于对象实例3、5直接虽然连通,但并没有任何一个GC Roots与之相连,这便是GC Roots不可达的对象,这就是GC需要回收的垃圾对象。

  到这里,相信大家应该能彻底明白引用计数算法和可达性算法的区别吧。

  再回过头来看看最前面的实例,GcObject实例1和实例2虽然从引用计数虽然都不为0,但从可达性算法来看,都是GC Roots不可达的对象。

  总之,对于对象之间循环引用的情况,引用计数算法,则GC无法回收这两个对象,而可达性算法则可以正确回收。

  参考资料:https://blog.csdn.net/lamp_zy/article/details/53212909

java垃圾回收机制--可达性算法的更多相关文章

  1. 【转载】Java垃圾回收机制

    原文地址:http://www.importnew.com/19085.html Java垃圾回收机制 说到垃圾回收(Garbage Collection,GC),很多人就会自然而然地把它和Java联 ...

  2. Java垃圾回收机制_(转载)

    Java垃圾回收机制 说到垃圾回收(Garbage Collection,GC),很多人就会自然而然地把它和Java联系起来.在Java中,程序员不需要去关心内存动态分配和垃圾回收的问题,这一切都交给 ...

  3. 【Java】Java垃圾回收机制

    Java垃圾回收机制 说到垃圾回收(Garbage Collection,GC),很多人就会自然而然地把它和Java联系起来.在Java中,程序员不需要去关心内存动态分配和垃圾回收的问题,这一切都交给 ...

  4. Java垃圾回收机制(Garbage Collection)

    引用博客地址:http://www.cnblogs.com/ywl925/p/3925637.html 以下两篇博客综合描述Java垃圾回收机制 第一篇:说的比较多,但是不详细 http://www. ...

  5. Java垃圾回收机制(GC策略)

    Java垃圾回收机制(GC策略) 核心:1,哪些是垃圾?[怎么确定这个是垃圾]:2,如何回收垃圾?[怎么更好收垃圾]. Java语言相对于C++等语言有一个自动垃圾回收机制,只用管使用[实例化对象], ...

  6. 图解Java 垃圾回收机制

    摘要: Java技术体系中所提倡的 自动内存管理 最终可以归结为自动化地解决了两个问题:给对象分配内存 以及 回收分配给对象的内存,而且这两个问题针对的内存区域就是Java内存模型中的 堆区.关于对象 ...

  7. 【转】深入理解 Java 垃圾回收机制

    深入理解 Java 垃圾回收机制   一.垃圾回收机制的意义 Java语言中一个显著的特点就是引入了垃圾回收机制,使c++程序员最头疼的内存管理的问题迎刃而解,它使得Java程序员在编写程序的时候不再 ...

  8. 深入理解java垃圾回收机制

    深入理解java垃圾回收机制---- 一.垃圾回收机制的意义 Java语言中一个显著的特点就是引入了垃圾回收机制,使c++程序员最头疼的内存管理的问题迎刃而解,它使得Java程序员在编写程序的时候不再 ...

  9. java 垃圾回收机制 引用类型

    Java语言的一个重要特性是引入了自动的内存管理机制,使得开发人员不用自己来管理应用中的内存.C/C++开发人员需要通过malloc/free 和new/delete等函数来显式的分配和释放内存.这对 ...

随机推荐

  1. eclipse 创建 maven 项目时如何修改 web 的版本和 jdk 的版本

    eclipse 创建 maven 项目时如何修改 web 的版本和 jdk 的版本 在使用 eclipse 创建 maven 项目的时候,默认的 web.xml 的版本时 2.3,默认 jre 的版本 ...

  2. thinkphp5.0URL访问

    URL设计 ThinkPHP5.0在没有启用路由的情况下典型的URL访问规则是: http://serverName/index.php(或者其它应用入口文件)/模块/控制器/操作/[参数名/参数值. ...

  3. AciveX接口事件的手动实现

    为了理解ActiveX事件的运作原理,特意做了如下实验 初试化过程: try { CLSID clsid; HRESULT hr=::CLSIDFromProgID(L"MSWinsock. ...

  4. Java反序列化漏洞之殇

    ref:https://xz.aliyun.com/t/2043 小结: 3.2.2版本之前的Apache-CommonsCollections存在该漏洞(不只该包)1.漏洞触发场景 在java编写的 ...

  5. C# 消除累计误差的倒计时

    使用 C# 中自带的各种 timer 计时,都会有累计误差,以下代码实现了一种消除累计误差的方法,使得每次计时的误差,空值在 100 ms 以内(可以通过修改代码提升精度.) 对于精度要求在秒级别的简 ...

  6. Linux通过FTP上传文件到服务器

    1.如果没有安装ftp,可执行: 输入:yum -y install ftp,回车 等待安装完毕 2.连接服务器 输入:ftp 服务器IP,回车 根据提示输入用户名和密码 3.上传下载操作 1). 上 ...

  7. 2017/11/20 Leetcode 日记

    2017/11/14 Leetcode 日记 442. Find All Duplicates in an Array Given an array of integers, 1 ≤ a[i] ≤ n ...

  8. go语言学习框架

    根据框架学习 https://books.studygolang.com/gopl-zh/ 看电子书 http://shinley.com/index.html

  9. FastReport.Net使用:[31]使用带参查询及存储

    带参查询 1.在数据列表中创建一个名为姓名的参数. 然后使用一个对话框,将文本框的ReportParameter(报表参数)选为参数中的姓名. 给童鞋们的一个题目:这里可以改为下拉框,学生列表从数据库 ...

  10. 什么是DQL、DML、DDL、DCL

    SQL(Structure Query Language)语言是数据库的核心语言. SQL的发展是从1974年开始的,其发展过程如下: 1974年-----由Boyce和Chamberlin提出,当时 ...