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进行垃圾回收的时候总会被回收。

posted @ 2021-04-02 19:04  TidalCoast  阅读(604)  评论(0编辑  收藏  举报