Java的四种引用方式
Java的四种引用方式
参考博客:https://www.cnblogs.com/huajiezh/p/5835618.html
一.为什么要设置不同种类的引用类型
jvm是通过GC机制自动管理内存空间的,不同于c/c++手动释放空间,所以我们得让jvm虚拟机知道哪些对象应该在什么时候释放,哪些对象还不能释放,设置不同的引用类型就是为了让jvm虚拟机更好地去判断这些情况。程序员也可以通过设置不同类型的引用来决定对象的生命周期,利于jvm回收,使内存应用效率更大化。
二.四种引用类型的概念
1.强引用
强引用:创建一个对象,并且把这个对象赋值给一个引用变量。
例如:
Object obj =new Object(); String str ="hello";
obj和str这两个引用变量为强引用,当强引用有引用变量指向时,jvm永远也不会对其进行回收,就算内存溢出了也不会,jvm宁愿抛出OOM错误也不会收回。
1 /* 2 -xmx2m 3 */ 4 public static void main(String[] args) { 5 Object object = new Object(); 6 Byte[] bytes = new Byte[10 * 1024 * 1024]; 7 }
把jvm的最大堆大小设置成8m,创建一个10m大小的byte数组,内存肯定会溢出,jvm会抛出OOM错误但是不会回收object指向的对象,只有当整个程序运行完之后,object和bytes被销毁后,它们指向的对象才会被Jvm回收,这就是强引用。
所以如果想要手动中断强引用和某一个对象之间的关联,可以显示地将引用赋值为null,这样jvm就会在合适时机自动回收这些对象(Vector类的clear方法中就是通过将引用赋值为null来实现清理工作的)。
2.软引用
一个对象如果是软引用,当堆内存足够时,垃圾回收器就不会对其进行回收,只有在内存空间不足,要迫切释放空间的时候,jvm才会释放软引用的对象。在软引用对象被回收之前,这些对象就可以被使用。
我们可以通过SoftReference关键字来软引用对象,它的一个实例保存对一个java对象的软引用,该软引用的存在不妨碍垃圾回收线程对java对象的回收。SoftReference类提供了一个方法get(),可以获取对象的强引用,也就是对象的地址。如果,垃圾回收线程回收该java对象之后,get()方法将会返回null。
设置虚拟机参数,使jvm堆大小最多为20m,然后for循环往list数组里添加5次大小为4m的byte数组,byte数组为软引用。代码如下:
1 /* 2 -Xmx20m -XX:+PrintGCDetails -verbose:gc 3 */ 4 public static void main(String[] args) { 5 List<SoftReference<byte[]>> list = new ArrayList<>(); 6 for(int i=0; i<5; i++){ 7 SoftReference<byte[]> ref = new SoftReference<>(new byte[4*1024*1024]); 8 System.out.println(ref.get()); 9 list.add(ref); 10 System.out.println(list.size()); 11 } 12 System.out.println("循环结束:" + list.size()); 13 for(SoftReference<byte[]> ref : list){ 14 System.out.println(ref.get()); 15 } 16 }
运行结果:
可以看出每次添加byte数组之后,因为byte数组是软引用,所以jvm调用gc将其回收了。在添加完之后,再次遍历list数组,我们可以发现list数组前四个值全是null,只有最后一个有地址,很明显前面四个都被垃圾回收掉了。
强可及对象
//强可及对象,有两个引用路径,一个是来自SoftReference对象的软引用, 一个来自变量softReference的强引用 Object obj = new Object(); SoftReference softReference = new SoftReference(obj); obj = null;//结束强引用
结束了obj的强引用之后,obj就是一个软引用对象。
obj = softReference.get();//重新获取强引用
也可以在垃级回收之前,通过让obj重新获取强引引用来避免被垃圾回收
ReferenceQueue引用队列
SoftReference对象除了具有保存软引用的特殊性之外,也具有Java对象的一般性。所以如果当SoftReference对象get方法返回null,这个对象不再具有价值了,为了避免大SoftReference对象带来的内存泄漏,在java.lang.ref包里还提供了ReferenceQueue对象队列。果在创建SoftReference对象的时候,使用了一个ReferenceQueue对象作为参数提供给SoftReference的构造方法,那么当这个SoftReference所软引用的aMyOhject被垃圾收集器回收的同时,ref所强引用的SoftReference对象被列入ReferenceQueue
1 ReferenceQueue queue = new ReferenceQueue(); 2 SoftReference ref=new SoftReference(aMyObject, queue);
ReferenceQueue中保存的都是失去了软引用的SoftReference对象,我们可以调用队列的poll()方法,查看队列最前面的那个Reference对象。利用这个方法,我们可以检查哪个SoftReference所软引用的对象已经被回收。于是我们可以把这些失去所软引用的对象的SoftReference对象清除掉。
SoftReference ref = null; while ((ref = (EmployeeRef) q.poll()) != null) { // 清除ref }
3弱引用
弱引用一般用来描述非必需对象,jvm进行垃圾回收的时候,不管内存状态是否充足,都会回收被弱引用关联的对象,使用WeakReference类来表示弱引用。
例子:
1 public class WeakUse { 2 3 4 public static void main(String[] args) { 5 WeakReference<People> reference =new WeakReference<People> 6 (new People("aaa", 11)); 7 System.out.println(reference.get()); 8 System.gc();//通知GVM回收资源 9 System.out.println(reference.get()); 10 } 11 } 12 13 class People{ 14 15 public String name; 16 public int age; 17 18 public People(String name, int age) { 19 this.name = name; 20 this.age = age; 21 } 22 23 @Override 24 public String toString() { 25 return "people{" + 26 "name='" + name + '\'' + 27 ", age=" + age + 28 '}'; 29 } 30 }
输出结果:
可以看到调用了gc之后,弱引用对象reference被垃圾回收了,所以第二个输出语句输出null
如果存在强引用和弱引用同时关联一个对象,那么这个对象是不会被jvm回收的,这点和软引用相同。
修改上述代码:
//强引用和弱引用的关联引用 People people = new People("aaa", 11); WeakReference<People> reference = new WeakReference<>(people); System.out.println(reference.get()); System.gc();//通知GVM回收资源 System.out.println(reference.get());
输出结果:
可以看出这个对象并没有被垃圾回收。
弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被JVM回收,这个软引用就会被加入到与之关联的引用队列中。
4虚引用
虚引用不会影响对象的生命周期,如果一个对象与虚引用关联,就和没有引用与之关联是一样的,在合适的时间就被垃圾回收。
虚引用必须和引用关联队列使用,在垃圾回收器准备回收虚引用对象的时候,就和把这个对象加入到与之关联的引用队列中,程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。
1 ReferenceQueue<String> queue = new ReferenceQueue<String>(); 2 PhantomReference<String> pr = new PhantomReference<String> 3 (new String("hello"), queue); 4 System.out.println(pr.get());
输出结果:null
三.总结
我们平时用的最多的就是强引用,其次是软引用和弱引用,后两者一般用来描述非必需的对象,以此来控制对象的生命周期。软引用关联的对象只有在jvm堆内存不足时,才会被垃圾回收,而弱引用关联的对象在jvm进行垃圾回收的时候总会被回收。