跟叶子猿学习JVM(四)垃圾回收机制(一)——引用计数算法

垃圾回收比较核心的几个问题是

1.如何判定对象为垃圾对象?

可使用算法:引用计数法、可达性分析法

2.如何回收?

回收策略:标记-清除算法、复制算法、标记-整理算法、分代收集算法

垃圾回收器:Serial、Parnew、Cms、G1

3.什么时候回收?

 

本文学习引用计数算法


引用计数算法

在A对象(在堆内存中)中添加一个引用计数器,当内存中有地方引用这个A对象的时候,引用计数器的值就+1,当引用失效(例如:对象赋值为null)的时候,引用计数器的值就-1,当对象没有被引用的时候,引用计数器值为0,满足垃圾回收条件。

但是,使用此算法的比较少,因为对象循环引用的时候会有问题,如下图:A被B、C引用,A的引用计数器的值为2,B被A、D引用,B的引用计数器的值为2,当①、②两个引用断掉后,此时只剩下③、④两个引用,那么此时A、B的引用计数器的值都为1,不满足垃圾回收条件,不会被回收。但是很明显A,B除了彼此引用并无他用,也就说明这A、B现在就是垃圾对象。由于引用计数器的值都不为0,一直都不会被回收。就会出现问题。

上述的引用计数算法不常用,我们尝试通过代码模拟上述场景,通过程序调用看看JDK8中的垃圾回收器是否使用了引用计数器算法:如果demo1,demo2未被回收了,则证明JDK8垃圾回收使用了引用计数算法。反之未使用。

package cn.com.jvm.demo;

import java.util.ArrayList;

import java.util.List;

public class TestDemo {



      //定义全局变量

      private Object instance;



      public static void main(String[] args) {

           //定义两个对象

           TestDemo demo1 = new TestDemo();

           TestDemo demo2 = new TestDemo();

          

           //让两个对象相互引用

           demo1.instance = demo2;

           demo2.instance = demo1;

          

           //断掉外部引用

           demo1 = null;

           demo2 = null;

          

           //调用垃圾回收器

           System.gc();

      }

}

在程序执行前我们先设置程序执行垃圾回收时打印垃圾回收信息

测试类右键→Run As→Run Configurations

设置打印参数-verbose:gc -XX:+PrintGCDetails

此时运行程序输出如下(回收前占用1474K内存,回收后占616K内存):

其实直接看不出来垃圾回收机制是否回收了我们想要回收的demo1,demo2对象,但是我们可以通过在对象的构造方法中,额外占用一些空间,再来查看垃圾回收信息

测试类中添加构造方法如下:


public TestDemo() {

           //在执行构造方法时候,占用一块20M的内存空间

           byte [] m = new byte[20 * 1024 * 1024];

      }

再次执行程序,打印日志如下(回收前占用21954K内存,回收后占600K内存):

回收前的内存大小由1474K增长到21954K,回收后内存大小基本一致(616K~600K)由此可以看出demo1,demo2已经被回收,可见JDK8中的垃圾回收机制并未使用引用计数算法。

猜你喜欢

转载自blog.csdn.net/Peacock__/article/details/88838155
今日推荐