JVM垃圾回收之—强软弱虚引用

1.强软弱虚引用介绍

JDK1.2之后,显示支持 强、软、弱、虚 四种类型引用

(1)Strong Reference 强引用,只要强引用还存在:JVM 就不会回收这种对象

所谓强引用就是我们最常使用的,类似于 User u = new  User()

那么就说 u 持有 User 对象的强引用:且只要 User 对象还被u持有,那么就不会被回收

 

(2)Soft Reference 软引用,在系统内存溢出之前,会把软引用持有的对象回收

如果这时内存还是不够,才会发生内存溢出。像很多组件就设计使用软引用来持有缓存对象。

 

(3)Weak Reference 弱引用,被弱引用关联的对象只能存活到下一次JVM垃圾回收之前

 

(4)Phantom Reference 虚引用,最弱的一种引用关系,你甚至都不能使用get方法

从虚引用获取其关联的对象

 

2.JVM 如何回收强引用类型对象

package com.yli.jvm;

/**
 * Strong Reference 强引用:由 new Objec() 形式创建的对象 
* 如果谁持有该对象的引用,那么就是强引用,比如:
* User u = new User(); 那么就说 u 持有 User 对象的强引用
* 所以很好理解强引用:他就是我们最常使用的引用
* * 那么强引用对象在什么时候才会被 JVM 回收呢?
* 当没有任何引用到达该对象时,JVM会自行决定的何时回收该对象
* * 比如上述 User 对象,如果我们设置 u = null;
* 那么在这种最简单情况下:我们说没有任何引用可到达之前创建的 User 对象
* 这时 JVM 会对 User 对象进行第一次标记,并将标记后的对象放入一个低优先级队列
* 再JVM下次GC之前又会进行第二次标记,如果对象没有实现finalize方法
* 那么 JVM 就认定 User 对象已经可以回收了!
* * 从上面的描述中发现什么特点了么:就是 finalize 方法
* 如果你再这个方法里面 拯救 User 对象,那意味着User可以复活的... * * * 新生代10M,初始堆和最大堆都是20M
* -Xmn10M -Xms20M -Xmx20M * * @author yli */ public class TestStrongReference { private static int _8MB = 8 * 1024 * 1024; private static TestStrongReference test = new TestStrongReference(); public static void main(String[] args) { // testOOM(); testSaveSelf(); } /** * 这个方法用来演示 GC 过程
* 即如果实现了 finalize 方法,在GC过程中会调用一次
* 在这次调用过程中对象有机会复活自己,如果没有复活成功
* 那么就没机会了,因为finalize方法只会执行一次! */ private static void testSaveSelf() { try { // 设置为 null,再运行 gc 方法 // 可以明显发现 finalize 方法被执行 test = null; System.gc(); // 延迟500ms 等待 gc 完成 Thread.sleep(500); // 成功复活自己 if (test == null) { System.out.println("test 对象已死!"); } else { System.out.println("test 对象活着!"); } // 重复来一次上述过程 test = null; // 这一次 finalize 不会执行了,因为已经执行过一次了! System.gc(); // 延迟500ms 等待 gc 完成 Thread.sleep(500); // 复活失败 if (test == null) { System.out.println("test 对象已死!"); } else { System.out.println("test 对象活着!"); } } catch (InterruptedException e) { e.printStackTrace(); } } /** * 简单测试堆内存溢出
* 这里虽然也会执行GC,并且会运行 finalize 方法
* 但明显没有啥内存可回收:因为obj1持有强引用且不释放
* 所以再次创建 obj2 时会内存溢出 * */ private static void testOOM() { // 消耗 8M 内存 byte[] obj1 = new byte[_8MB]; System.out.println("---->内存还不会溢出!"); try { test = null; System.gc(); // 延迟500ms 等待 gc Thread.sleep(500); byte[] obj2 = new byte[_8MB]; } catch (OutOfMemoryError err) { System.out.println("---->内存会溢出!"); err.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } /** * 如果JVM进行GC操作,肯定会打印如下消息 */ @Override protected void finalize() throws Throwable { super.finalize(); System.out.println("-------->JVM 回收内存!"); // 拯救 test 对象 test = this; } }


3.JVM 如何回收软引用对象

package com.yli.jvm;

import java.lang.ref.SoftReference;

/**
 * 软引用SoftReference:引用关系比强引用低一个等级
* 当系统内存不足可能导致内存溢出之前,JVM会把软引用关联的对象回收
* 如果这时系统内存仍然不足才会发生内存溢出!
* * 新生代5M,初始堆和最大堆都为10M(即不允许堆内存扩展)
* -Xmn5M -Xms10M -Xmx10M * * @author yli */ public class TestSoftReference { private static int _8MB = 8 * 1024 * 1024; public static void main(String[] args) throws InterruptedException { // 消耗8MB内存空间 SoftArticle article = new SoftArticle(new byte[_8MB]); SoftReference softRef = new SoftReference(article); // article 是 new SoftUser()创建的对象,因此他持有SoftUser的【强引用】 // 此处设置为 article = null非常重要 // 这保证了刚才创建的 SoftUser对象只被 【softRef】持有引用 // 并且是持有:【软引用】 article = null; // 测试:由 【软引用】 关联的对象是否还活着! testAliveOrDead(softRef); // 重新创建一个强引用对象:需再次消耗8MB内存空间 article = new SoftArticle(new byte[_8MB]); // 再次测试:由 【软引用】 关联的对象是否还活着! testAliveOrDead(softRef); } // 从软引用的get方法就能判断对象是否已经被回收! private static void testAliveOrDead(SoftReference softRef) { if (null == softRef.get()) { System.out.println("额...我已经被JVM回收...已经死亡!"); } else { System.out.println("额...我还活着!"); } } } class SoftArticle { private byte[] content; public SoftArticle(byte[] content) { this.content = content; } public byte[] getContent() { return this.content; } }


4.JVM 如何回收弱引用对象

package com.yli.jvm;

import java.lang.ref.WeakReference;

/**
 * 弱引用:对象只能存活到下一次垃圾回收之前
* * @author yli */ public class TestWeakReference { public static int count = 0; private static TestWeakReference tref = new TestWeakReference(); // user 持有 WeakUser 对象的引用 private static WeakUser user = new WeakUser("ali"); public static void main(String[] args) throws InterruptedException { /** * 如果一个对象只被WeakReference关联
* 那么该对象只能存活到下一次垃圾回收之前 */ // user 对象被 weakRef 即一个弱引用关联 WeakReference weakRef = new WeakReference(user); // 这样保证 只有 weakRef 持有 WeakUser 对象的引用 user = null; System.out.println("user--->" + weakRef.get()); count++; tref = null; System.gc(); Thread.sleep(500); System.out.println("user--->" + weakRef.get()); count++; tref = new TestWeakReference(); tref = null; System.gc(); Thread.sleep(500); System.out.println("user--->" + weakRef.get()); } @Override protected void finalize() throws Throwable { super.finalize(); System.out.println(String.format("第%s次内存回收!", count)); } } class WeakUser { private String name; public WeakUser(String n) { this.name = n; } public String toString() { return String.format("{name:%s}", name); } }


5.JVM 如何回收虚引用对象

package com.yli.jvm;

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;

/**
 * 虚引用:最弱的引用关系
* 使用虚引用,你可能只是想在垃圾回收之前收到通知!
* * JVM 参数设置为查看GC过程详细信息
* -XX:+PrintGCDetails * * @author yli */ public class TestPhantomReference { public static void main(String[] args) { /** * 这里只演示:一旦使用虚引用关联该对象,就无法从虚引用获取该对象了! */ PhantomUser user = new PhantomUser(); ReferenceQueue queue = new ReferenceQueue(); PhantomReference pref = new PhantomReference(user, queue); // 打印为 null System.out.println(pref.get()); } } class PhantomUser { }


 

你可能感兴趣的:(Java)