Java中有四种引用类型:强引用、软引用、弱引用、虚引用
Java为什么要设计这四种引用
Java的内存分配和内存回收 都不需要程序员负责 都是由伟大的JVM去负责
一个对象是否可以被回收 主要看是否有引用指向此对象 说的专业点 叫可达性分析
Java设计这四种引用的主要目的有两个:
1)可以让程序员通过代码的方式来决定某个对象的生命周期
2)有利用垃圾回收
1 强引用
new出来的对象都是强引用 垃圾回收器绝不会回收它
比如 **Object o = new Object()**;
当内存空间不足 Java虚拟机宁愿抛出OutOfMemoryError错误 使程序异常终止 也不会靠随意回收具有强引用的对象来解决内存不足问题
那么什么时候才可以被回收呢?当强引用和对象之间的关联被中断了 就可以被回收了
我们可以手动把关联给中断了 方法也特别简单:
o = null;
在实际的开发中 看到有一些对象被手动赋值为NULL 很大可能就是为了“特意提醒”JVM这块资源可以进行垃圾回收了
2 软引用(SoftReference)
下面先来看看如何创建一个软引用:
SoftReference
软引用就是把对象用SoftReference包裹一下 当我们需要从软引用对象获得包裹的对象 只要调用get()方法就可以了
User user = soft.get();
软引用特点:
当内存不足 会触发JVM的GC 如果GC后 内存还是不足 就会把软引用的包裹的对象给干掉 也就是只有在内存不足 JVM才会回收该对象
只要垃圾回收器没有回收它 该对象就可以被程序使用
软引用作用:
比较适合用作缓存 当内存足够 可以正常的拿到缓存 当内存不够 就会先干掉缓存 不至于马上抛出OOM
软引用可以和一个引用队列(ReferenceQueue)联合使用
如果软引用所引用的对象被垃圾回收 Java虚拟机就会把这个软引用加入到与之关联的引用队列中
3 弱引用(WeakReference)
弱引用的使用方式和软引用类似
WeakReference
弱引用与软引用的区别在于:弱引用的对象拥有更短暂的生命周期
弱引用特点:
不管内存是否足够 只要发生GC 都会被回收
不过 由于垃圾回收器是一个优先级很低的线程 因此不一定会很快发现那些只具有弱引用的对象
弱引用的用处
比如ThreadLocal、WeakHashMap
弱引用可以和一个引用队列(ReferenceQueue)联合使用
如果弱引用所引用的对象被垃圾回收 Java虚拟机就会把这个弱引用加入到与之关联的引用队列中
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class TestMain{
public static void main(String[] args) {
//强引用
User user = new User();
//软引用
SoftReference soft = new SoftReference(new User());
//弱引用
WeakReference weak = new WeakReference(new User());
System.out.println("new = "+user);
System.out.println("soft = "+soft.get());
System.out.println("weak = "+weak.get());
//垃圾回收
System.gc();
System.out.println("new = "+user);
System.out.println("soft = "+soft.get());
//弱引用的被回收
System.out.println("weak = "+weak.get());
//模拟内存不足
System.out.println("内存不足");
List list = new ArrayList<>();
try {
while (true) {
list.add(new byte[1024*1024*5]);
}
}catch (Throwable throwable){
System.out.println("new = "+user);
System.out.println("soft = "+soft.get());
System.out.println("weak = "+weak.get());
}
}
}
4 虚引用(理解就行)
虚引用又被称为幻影引用 我们来看看它的使用:
ReferenceQueue queue = new ReferenceQueue();
PhantomReference<Uesr> reference = new PhantomReference<Uesr>(new Uesr(), queue);
System.out.println(reference.get());
//得到的结果为null
//看看get方法的源码
public T get() {
return null;
}
1)即无法通过虚引用来获取对一个对象的真实引用
2)虚引用必须与ReferenceQueue一起使用
当GC准备回收一个对象 如果发现它还有虚引用 就会在回收之前 把这个虚引用加入到与之关联的ReferenceQueue中**
import java.lang.ref.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class TestMain{
public static void main(String[] args) {
ReferenceQueue queue = new ReferenceQueue();
List<Object> list = new ArrayList<>();
PhantomReference<User> phantomReference = new PhantomReference<User>(new User(),queue);
new Thread(
new Runnable() {
@Override
public void run() {
while (true) {
list.add(new byte[1024 * 1024 * 1024 * 5]);
try {
Thread.sleep(500);
}catch (Exception e){
e.printStackTrace();
}
System.out.println(phantomReference.get());
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
//若从引用队列中能拿到虚引用 说明对象已经被回收了
Reference reference = queue.poll();
if (reference != null) {
//若为null 说明已经被GC回收了
System.out.println("虚引用被回收了:" + reference.get());
//输出null
}
}
}
}).start();
}
}
从运行结果可以看到:当发生GC 虚引用就会被回收 并且会把回收的对象放到ReferenceQueue中
虚引用有什么用呢?在NIO中 就运用了虚引用管理堆外内存