# JVM 垃圾回收-01 可达性分析与强弱软虚引用详解

可达型分析

可达性分析的理论

基本思路:
通过一系列GC Roots的根对象作为起始节点集, 根据引用关系向下搜索, 
搜索过程中走过的路径称为引用链,所有在这个引用链上的对象都是可达对象,
而其他的没有与GC Roots根对象关联的单独存在的引用链上的对象则为不可达对象

图示:


image
在内存中的场景:
上图的 object1 中有字段object2 , object3 的引用, 
当我们把object1的引用ref赋值为null, 
那么object1就变为了 上图右侧的不可达对象的图例了..需要被gc回收掉了,如下图引用
image
代码实现举例子:


@Data
public class Test {
    public Test instance;
    public int i;
    public Test(int i) {
        this.i = i;
    }
}


public static void main(String[] args) {
    Test test0 = new Test(0);
    Test test1 = new Test(1);
    Test test2 = new Test(2);
    test0.instance=test1;
    test1=test2;
    System.out.println(test0.getInstance().getI());
}

执行结果 1
为什么不是2呢,test1已经被test2赋值了呀!
这是因为引用变量只能是指向某个对象的,而不能是指向引用的
所以test0.instance 指向的是test1在堆中的对象,
所以在后面改变了test1的指向时, 并没有影响之前的指向.


public static void main(String[] args) {
    Test test0 = new Test(0);
    Test test1 = new Test(1);
    Test test2 = new Test(2);
    test0.instance=test1;
    test1.instance=test2;
    test0=null;
}

ps: test0=null则 test0的原映射对象是不在引用链上了,会被gc回收.
且test0.instance的由上例子可知是引用的对象test1, 但这里不会回收test1,
因为test1也自己定义了一个根对象, 所以test1还是在test1引用链上,但不在test0的引用链上了,
如果将test1=null也这样设置,那么test1也将会被gc回收;

问题:那么在平时的代码中我们要不要在使用后对象就将对象置空呢?
这个问题要看 JVM 垃圾回收-判断对象是否可以回收 中的描述了

可达性根枚举对象

GC Roots的对象
1. 虚拟机栈中的引用的对象            如:Object o = new Object(); o即为虚拟栈中的引用对象
2. 方法区中类静态属性引用的对象       如:public static String static_str="111";  static_str 即为引用对象
3. 方法区中常量引用的对象
4. 本地方法栈中 JNI(native方法)引用的对象
5. 被同步锁synchronize持有的对象

ps:这里提到了很多引用,我们下面将详细描述引用的分类,引用之所以进行设定不同的分类, 
   是因为在内存中数据的要求是多样的,
   比如 我们希望在内存充足的情况下,保留这些类,但内存不足的时候,就进行回收(缓存机制)

引用是如何分类

强引用

使用方法

正常的写的java代码都是强引用99.999%
Object o = new Object();

这种就是强引用了,是不是在代码中随处可见,最亲切。 
只要某个对象有强引用与之关联,这个对象永远不会被回收
即使内存不足,JVM宁愿抛出OOM,也不会去回收

回收的方法:

o = null;

示例:

我们需要新写一个类,然后重写finalize方法

public class Student {
    @Override
    protected void finalize() throws Throwable {
        System.out.println("Student 被回收了");
    }
}

public static void main(String[] args) {
        Student student = new Student();
        student = null;
        System.gc();
}

运行结果:
Student 被回收了

注释:finalize方法是当gc时,系统来调用该方法
释放该对象在堆中占用的内存. 该方法在Object中

软引用

使用方法

用java.lang.ref.SoftReference 进行包装
SoftReference<Student>studentSoftReference=new SoftReference<Student>(new Student());

回收方法

gc自动处理
当内存不足,会触发JVM的GC,如果GC后,内存还是不足,就会把软引用的包裹的对象给干掉
也就是只有在内存不足,JVM才会回收该对象

示例:

修改idea的堆内存大小 -Xmx20m  最大堆内存为20m , 设置内存追踪 -XX:+PrintGCDetails

  SoftReference<byte[]> softReference = new SoftReference<byte[]>(new byte[1024*1024*10]);
  System.out.println(softReference.get());
  System.gc();
  System.out.println(softReference.get());
  byte[] bytes = new byte[1024 * 1024 * 10];
  System.out.println(softReference.get());
  
  创建一个软引用对象,里面包裹了byte[],byte[]占用了10M,然后又创建了10Mbyte[]
  
结果:

[GC (Allocation Failure)  5632K->1393K(19968K), 0.0049815 secs]
[B@763d9750
[GC (System.gc())  16187K->12252K(19968K), 0.0014227 secs]
[Full GC (System.gc())  12252K->11862K(19968K), 0.0101228 secs]
[B@763d9750
[GC (Allocation Failure)  12032K->11926K(19968K), 0.0004047 secs]
[GC (Allocation Failure)  11926K->11926K(19968K), 0.0002842 secs]
[Full GC (Allocation Failure)  11926K->11784K(19968K), 0.0049854 secs]
[GC (Allocation Failure)  11784K->11784K(19968K), 0.0002981 secs]
[Full GC (Allocation Failure)  11784K->1495K(16896K), 0.0066588 secs]
null
 
分析: 当gc回收时, 软 

注释:一般是缓存使用

弱引用

使用方法

java.lang.ref.WeakReference包装
WeakReference<byte[]> weakReference = new WeakReference<byte[]>(new byte[1024*1024*10]);
System.out.println(weakReference.get());

回收方式

System.gc();
不管内存是否足够,只要发生GC,都会被回收

示例

WeakReference<byte[]> weakReference = new WeakReference<byte[]>(new byte[1]);
System.out.println(weakReference.get());
System.gc();
System.out.println(weakReference.get());

结果:
[GC (Allocation Failure)  5632K->1410K(19968K), 0.0016663 secs]
[B@763d9750
[GC (System.gc())  5726K->2034K(19968K), 0.0015112 secs]
[Full GC (System.gc())  2034K->1914K(19968K), 0.0113147 secs]
null

注释:弱引用在很多地方都有用到,比如ThreadLocal、WeakHashMap。

虚引用

使用方法:

 当发生GC,虚引用就会被回收,并且会把回收的通知放到ReferenceQueue中
 ReferenceQueue queue = new ReferenceQueue();
 PhantomReference<byte[]> reference = new PhantomReference<byte[]>(new byte[1], queue);
 System.out.println(reference.get());
 
 作用时,当gc回收的时候会产生一条记录到ReferenceQueue中

回收方式:

System.gc() 自动回收

示例:

@Data
@Builder
public class Student {
    @Override
    protected void finalize() throws Throwable {
        System.out.println("Student 被回收了");
    }
}


public static void main(String[] args) {
        ReferenceQueue queue = new ReferenceQueue();
        List<byte[]> bytes = new ArrayList<>();
        PhantomReference<Student> reference = new PhantomReference<Student>(new Student(),queue);
        new Thread(() -> {
            for (int i = 0; i < 100;i++ ) {
                bytes.add(new byte[1024 * 1024]);
            }
        }).start();

        new Thread(() -> {
            while (true) {
                Reference poll = queue.poll();
                if (poll != null) {
                    System.out.println("虚引用被回收了:" + poll);
                }
            }
        }).start();
        Scanner scanner = new Scanner(System.in);
        scanner.hasNext();
    }

结果: 

[GC (Allocation Failure)  5632K->1372K(19968K), 0.0011861 secs]
[GC (Allocation Failure)  7004K->2185K(19968K), 0.0017764 secs]
[GC (Allocation Failure)  7205K->6394K(19968K), 0.0028557 secs]
[GC (Allocation Failure)  11653K->11554K(19968K), 0.0027832 secs]
[Full GC (Ergonomics)  11554K->11374K(19968K), 0.0134628 secs]
[Full GC (Ergonomics)  16624K->16456K(19968K), 0.0055213 secs]
[Full GC (Ergonomics)  18620K->18488K(19968K), 0.0098597 secs]
[Full GC (Allocation Failure)  18488K->18402K(19968K), 0.0115410 secs]
Student 被回收了
[Full GC (Ergonomics)  18949K->17924K(19968K), 0.0106182 secs]
Exception in thread "Thread-0" java.lang.OutOfMemoryError: Java heap space
    at com.logistic.JVMTest.lambda$main$0(JVMTest.java:16)
    at com.logistic.JVMTest$$Lambda$1/747464370.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)
虚引用被回收了:java.lang.ref.PhantomReference@5eb93cd7

注释:
第一个线程往集合里面塞数据,随着数据越来越多,肯定会发生GC
第二个线程死循环,从queue里面拿数据,如果拿出来的数据不是null,就打印出来

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272

推荐阅读更多精彩内容