Java 基础之对象的四种引用-强软弱虚

Java 中对象的引用分为四种,可以让我们更好的保证程序运行时足够的内存,这也是面试时经常问到的题目,在此记录一下。

一、强引用

最开始学习的 Java 变量的声明方式其实就是强引用,这是最常用、最普遍的引用。

String str = new String("Hello World");

这其实就是强引用。如果一个对象具有强引用,GC 绝不会回收它。当内存不够用时,JVM 宁愿抛出 OOM 异常也不会回收强引用对象。只有显式将强引用置为 null 才可以释放对象。

如下代码,最终会抛出异常 Exception in thread "main" java.lang.OutOfMemoryError: Java heap space(自己测试时为了更快看到效果可以将 JVM 堆内存改小一下,如何设置可自行百度)。

package com.qinshou.resume.reference;

import java.util.ArrayList;
import java.util.List;

public class ReferenceDemo {
	private static boolean sStart = true;

	public static void main(String[] args) {
		strongReference();
	}

	public static void strongReference() {
		String str = new String("Hello World");
		List<String> list = new ArrayList<>();
		int index = 0;
		long startTime = System.currentTimeMillis();
		while (sStart) {
			list.add(new String("Hello World " + (index++)));
			System.out.println("执行时间--->" + (System.currentTimeMillis() - startTime));
			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

二、软引用

对于软引用,如果内存空间足够,GC 就不会回收它,如果内存不足了才会回收。

Java 中所有的引用都是 Reference 的子类,它是个抽象类,软引用对应的实现类是 SoftReference。我们可以将一个对象作为参数来创建 SoftReference 对象,这样就可以将这个对象的引用指定为软引用了。然后可以通过 SoftReference 对象的 get() 方法来获取传入的对象。

String str = new String("Hello World");
Reference<String> softReference = new SoftReference<>(str);
System.out.println("softReference.get--->" + softReference.get());

软引用、弱引用、虚引用都可以搭配 ReferenceQueue 来使用,当所引用的对象被 GC 回收时,虚拟机会把这个引用加入到这个引用队列中。我们可以利用这个 ReferenceQueue 来跟踪对象的生命周期,可以通过 poll() 或者 remove() 方法来获取被回收的引用,这两个方法的区别是一个阻塞,一个非阻塞。因为从队列中获取到的引用是被回收的引用,所以调用它的 get() 方法得到的永远是 null。

package com.qinshou.resume.reference;

import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.List;

public class ReferenceDemo {
	private static boolean sStart = true;

	public static void main(String[] args) {
		softReference();
	}

	public static void softReference() {
		String str = new String("Hello World");
		Reference<String> softReference = new SoftReference<>(str);
		System.out.println("softReference.get--->" + softReference.get());
		ReferenceQueue<String> referenceQueue = new ReferenceQueue<>();
		List<Reference<String>> list = new ArrayList<>();
		new Thread(new Runnable() {

			@Override
			public void run() {
				try {
					// poll() 方法会从队列中取去第一个引用,没有的话直接返回 null
					Reference<? extends String> reference = referenceQueue.poll();
					System.out.println("reference--->" + reference);
					// remove() 方法会从队列中取去第一个引用,没有的话会阻塞当前线程,直到有被回收的引用
					reference = referenceQueue.remove();
					System.out.println("reference--->" + reference);
					sStart = false;
					// 从队列中获取的引用,调用 get() 方法获取真实对象的时候永远为 null,因为已经被回收掉了
					Object object = reference.get();
					System.out.println("object--->" + object);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}

			}
		}).start();
		int index = 0;
		long startTime = System.currentTimeMillis();
		while (sStart) {
			list.add(new SoftReference<>(new String("Hello World " + (index++)), referenceQueue));
			System.out.println("执行时间--->" + (System.currentTimeMillis() - startTime));
			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

三、弱引用

弱引用跟软引用的区别的在于,软引用只会在内存空间不足时 GC 才会回收,而弱引用的话只要 GC 扫描到该引用所在内存区域,无论内存空间是否充足都会回收。

package com.qinshou.resume.reference;

import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;

public class ReferenceDemo {
	private static boolean sStart = true;

	public static void main(String[] args) {
		weakReference();
	}
	
	public static void weakReference() {
		String str = new String("Hello World");
		Reference<String> weakReference = new WeakReference<>(str);
		System.out.println("weakReference.get--->" + weakReference.get());
		ReferenceQueue<String> referenceQueue = new ReferenceQueue<>();
		List<Reference<String>> list = new ArrayList<>();
		new Thread(new Runnable() {

			@Override
			public void run() {
				try {
					Reference<? extends String> reference = referenceQueue.remove();
					sStart = false;
				} catch (InterruptedException e) {
					e.printStackTrace();
				}

			}
		}).start();
		int index = 0;
		long startTime = System.currentTimeMillis();
		while (sStart) {
			list.add(new WeakReference<>(new String("Hello World " + (index++)), referenceQueue));
			System.out.println("执行时间--->" + (System.currentTimeMillis() - startTime));
			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

上述代码的 while 循环,退出得会比使用软引用时要快很多。在 Android 中,使用弱引用是防止内存泄漏的一个常见手段。

四、虚引用

虚引用又称幽灵引用、影子引用,它无法通过 get 方法获取到实例,如果一个对象仅持有虚引用,那它跟没有引用一样。虚引用主要跟 ReferenceQueue 一起搭配使用,用来跟踪对象被 GC 回收。

package com.qinshou.resume.reference;

import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.ArrayList;
import java.util.List;

public class ReferenceDemo {
	private static boolean sStart = true;

	public static void main(String[] args) {
		phantomReference();
	}

	public static void phantomReference() {
		String str = new String("Hello World");
		PhantomReference<String> phantomReference = new PhantomReference<>(str, null);
		System.out.println("phantomReference.get--->" + phantomReference.get());
		ReferenceQueue<String> referenceQueue = new ReferenceQueue<>();
		List<Reference<String>> list = new ArrayList<>();
		new Thread(new Runnable() {

			@Override
			public void run() {
				try {
					Reference<? extends String> reference = referenceQueue.remove();
					sStart = false;
				} catch (InterruptedException e) {
					e.printStackTrace();
				}

			}
		}).start();
		int index = 0;
		long startTime = System.currentTimeMillis();
		while (sStart) {
			list.add(new PhantomReference<>(new String("Hello World " + (index++)), referenceQueue));
			System.out.println("执行时间--->" + (System.currentTimeMillis() - startTime));
			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

五、总结

合理使用 Java 提供的各种引用,可以更好的控制程序内存,优化我们的程序,减少 OOM 的风险。比如在设计一些缓存机制的时候,将缓存对象设置为软引用或虚引用,既可以实现高速缓存,又能避免一些内存问题。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值