强引用、软引用、弱引用、虚引用
java中将引用类型分为强引用、软引用、弱引用、虚引用。之所以要这么划分,还是为了GC时更好的对对象进行处理。因为jvm已经明确了各种引用的GC方式,所以谈四种引用最好是和GC一起聊才会有意义(个人愚见)。
强引用
java中正常声明的变量都是强引用,比如:
String str = "Hello"; String stri = new String("Hello");
强引用只要是在栈空间仍存在指向堆中的引用就不会被回收。
怎么回收强引用?
1.类变量,声明过以后,在不使用时应做null处理。这样GC才会处理他,至于是不是下一次GC则不一定了。
2.局部变量,基本会在方法或者代码块执行完成之后就会GC,如果被其他变量引用了,仍存在引用链则不会回收。
软引用
软引用需要使用java提供的SoftReference来包装,如下则是软引用的声明方式:
String str = "abc"; SoftReference<String> sf = new SoftReference<String>(str); String strRefe = sf.get(); System.out.println(strRefe); 输出结果: abc Process finished with exit code 0
怎么回收软引用?
1.软引用会在jvm报OOM之前,将所有软引用全部清除,也就是说jvm内存吃紧会去清理软引用。
2.GC在回收软可达对象时,会回收软引用,回收这些软引用的对象时会判断该对象是不是不可达了,可达则不会回收。
举个例子证明内存紧张时软应用不会一定回收,因为这些对象仍是可达的:
public class TestHeapOom { static class OOMObject{ String name; public OOMObject(String name){ this.name = name; } } public static void main(String[] args){ int n =0; List<OOMObject> list = new ArrayList<OOMObject>(); SoftReference<List<OOMObject>> sr = new SoftReference<List<OOMObject>>(list); System.out.println(sr.get().size()); while (true){ list.add(new OOMObject("a"+n)); System.out.println(sr.get().size()); } } }
程序输出:
1 1 ... 106063 106064 106065 106066 106067 106068 106069 Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded at com.example.demo.OOM.TestHeapOom.main(TestHeapOom.java:34) Process finished with exit code 1
输出结果解析:可以看到跑出的异常是“GC overhead limit exceeded”,该错误的意思是cpu98%时间在执行垃圾回收,但回收到的内存仍是非常小,会抛出该异常。如果这个程序不加软引用则抛出的是“Java heap space”,因为内存紧张程序就会试图回收软应用,则会一直运行GC,但是这些软应用仍是可达的,又回收不了,故会一直GC,却回收不到内存,就会有“GC overhead limit exceeded”这种异常。
弱引用
软引用需要使用java提供的WeakReference来包装,如下则是软引用的声明方式(此外java中的WearHashMap中的key默认就是弱引用,若是只有map引用这些对象,则会被回收):
WeakReference<String> wf = new WeakReference<String>(str); String strRew = wf.get(); System.out.println(strRew); 输出结果: abc Process finished with exit code 0
怎么回收弱引用?
会在下一次gc时直接回收弱引用内存(不一定是一次gc就全部回收,且该对象必须是不可达才会回收,使用上面的例子仍然可以证明)
虚引用
软引用需要使用java提供的WeakReference来包装,虚引用是无法通过get得到值的,得到的永远会是null,如下则是软引用的声明方式:
ReferenceQueue rq = new ReferenceQueue(); PhantomReference<String> pf = new PhantomReference<String>(str,rq); System.out.println(pf.get());
怎么回收虚引用?
虚引用会被直接回收,所以无法通过虚引用获得一个对象实例,但是被虚引用关联的对象何时回收其实不受任何影响,判断对象会不会回收依据仍然是jvm的可达性分析算法,为一个对象设置虚引用关联的唯一目的只是在这个对象被垃圾收集器回收时收到一个系统通知,判断虚引用关联的对象会不会回收仍然可用软引用中的例子。
————————————————
版权声明:本文为CSDN博主「归去来 兮」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/m0_46897923/article/details/109337874