java有四种引用类型分别是:强引用、软引用、弱引用、虚引用
四大引用设计作用
由于java的内存分配和内存回收,都是由JVM去负责。一个对象是否可以被回收,主要看是否有引用指向此对象,即可达性分析。由于我们程序员一般不去主动的调用GC方法,要想达到对某个对象的生命周期进行管理,就要灵活应用各种引用来创建对象。
-
设计目的:
-
- 可以让程序员通过代码的方式来决定某个对象的生命周期
- 有利用垃圾回收
强引用
使用最广泛的,也是最常见的。
Object o = new Object();
只要某个对象有强引用与之关联,这个对象就永远不会被回收,即使内存不足,JVM也只会抛出OOM也不去回收。
但是要是想让JVM去回收该对象,那么需要中断强引用和对象之间的关联。
- 中断关联
n=null;
为了验证,在此我们手动调用GC,看看强引用和对象之间的关联被中断,资源会不会被回收。
public class Student {
@Override
protected void finalize()throws Throwable{
System.out.println(" Student 被回收了");
}
public static void main(String[] args) {
Student student = new Student();
System.gc();
System.out.println("===============");
student = null;
System.gc();
}
}
- 结果:
Student 被回收了
-
注意
-
- 开发中不要重写finalize方法
- 开发中可以将一些对象手动赋值为NULL,可以提醒JVM将这些资源进行垃圾回收
软引用
- 创建一个软引用
SoftReference<Student> studentSoftReference = new SoftReference<Student>(new Student());
- 软引用就是把对象用SoftReference包裹一下,当我们需要从软引用对象获取包裹的对象,只要get一下就可以。
SoftReference<Student> studentSoftReference = new SoftReference<Student>(new Student());
Student student = studentSoftReference.get();
System.out.println(student);
-
特点:
-
- 当内存不足时,会触发JVM的GC,如果GC后,内存还是不足,就会把软引用的包裹的对象给回收掉
- 也就是说只有在内存不足,JVM才会回收该对象
-
验证:
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());
- 运行时需要加上JVM参数:-Xmx20M 代表最大的堆内存为20M
- 结果
null
- 当我们手动GC后,软引用对象包裹中的对象还没事,当我们创建一个10M的byte后,最大堆内存不够用了,就将软引用的对象给垃圾回收了。
- 作用:可以当做缓存,当内存足够的时候可以正常拿到缓存,当内存不够就会先干掉缓存,不至于出现OOM
import java.lang.ref.SoftReference;
import java.util.HashMap;
/**
* SoftRefenceCache
* @param <K> key的类型.
* @param <V> value的类型.
*/
public class SoftReferenceCache<K, V> {
private final HashMap<K, SoftReference<V>> mCache;
public SoftReferenceCache() {
mCache = new HashMap<K, SoftReference<V>>();
}
/**
* 将对象放进缓存中,这个对象可以在GC发生时被回收
*
* @param key key的值.
* @param value value的值型.
*/
public void put(K key, V value) {
mCache.put(key, new SoftReference<V>(value));
}
/**
* 从缓存中获取value
*
* @param key
*
* @return 如果找到的话返回value,如果被回收或者压根儿没有就返* 回null
*/
public V get(K key) {
V value = null;
SoftReference<V> reference = mCache.get(key);
if (reference != null) {
value = reference.get();
}
return value;
}
public static void main(String[] args) {
SoftReferenceCache<Integer, String> mPersonCache = new SoftReferenceCache<Integer, String>();
mPersonCache.put(0, ("zhong"));
mPersonCache.put(1, ("hu"));
// 去拿zhong
String p = (String) mPersonCache.get(1);
}
}
弱引用
弱引用和软引用类似,只是关键字换成了WeakReferce
WeakReference weakReference = new WeakReference<byte[]>(new byte[1024*1024*10]);
System.out.println(weakReference.get());
- 特点:不管内存是否够用,只要发生GC就会被回收(这也是与软引用之间最明显的区别)
- 用途:ThreadLocal、WeakHashMap。
虚引用
虚引用又称为幻影引用
ReferenceQueue queue = new ReferenceQueue();
PhantomReference<byte[]> reference = new PhantomReference<byte[]>(new byte[1],queue);
System.out.println(reference.get());
- 虚引用的使用和其他的还是有较大的区别。
- 结果:
null
- 因为其get方法直接返回了null
- 创建虚引用对象,我们除了把包裹的对象传了进去,还传了一个ReferenceQueue(队列)
- 虚引用必须与ReferenceQueue一起使用,当GC准备回收一个对象,如果发现它还有虚引用,就会在回收之前,把这个虚引用加入到与之关联的ReferenceQueue中。
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();
- 结果
虚引用被回收了:java.lang.ref.PhantomReference@5ac42552
第一个线程往集合中放数据,随着数据越来越多,肯定发送GC
第二个线程死循环,从queue中拿数据,如果拿出来的数据不是null那就打印出来
当发生GC的时候,虚引用就会被回收,并且会把回收的同志放到ReferenceQueue中
在NIO中,可以使用虚引用来管理堆外内存
最后
- 如果觉得看完有收获,希望能给我点个赞,这将会是我更新的最大动力,感谢各位的支持
- 欢迎各位关注我的公众号【java冢狐】,专注于java和计算机基础知识,保证让你看完有所收获,不信你打我
- 如果看完有不同的意见或者建议,欢迎多多评论一起交流。感谢各位的支持以及厚爱。