栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

浅谈java中的引用类型--强,软,弱,虚

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

浅谈java中的引用类型--强,软,弱,虚

浅谈java中的引用类型
  • 引用类型
    • 引用类型的分类
      • 强引用
        • 强引用API
      • 软引用----->>>
        • 软引用的API------>>>
      • 弱引用----->>>
        • 弱引用的API
        • 弱引用在在ThreadLocal类中的应用
        • 弱引用的生命周期---->>
      • 虚引用
        • 虚引用API

引用类型

定义: 由类型的实际值引用(类似于C中的指针)表示的数据类型。如果为某个变量分配一个引用类型,则该变量将引用(或“指向”)原始值。不创建任何副本。引用类型包括类、接口、委托和装箱值类型。

关于引用类型最大的父类(除了Object)

public abstract class Reference extends Object

引用类型的分类

在java中有四种引用类型,强,软,弱,虚

在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无法再使用这个对象。也就是说,只有对象处于(reachable)可达状态,程序才能使用它。

从JDK 1.2版本开始,对象的引用被划分为4种级别,从而使程序能更加灵活地控制对象的生命周期。这4种级别由高到低依次为:强引用、软引用、弱引用和虚引用。

Reference 的子类---->>
PhantomReference(虚引用),
SoftReference(软引用),
WeakReference(弱引用),
这些都跟GC回收有关,因为父类Reference 是跟GC有关的;

强引用

public abstract class Reference extends Object

引用对象的抽象基类。 这个类定义了 所有引用对象通用的操作。 因为引用对象是 与垃圾收集器密切合作实现,这个类可以 不能直接子类化。

强引用API

强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它

代码3----->

import java.io.IOException;

class N{
    @Override//该方法已弃用,但是为了演示一下,还是用一下吧!
    protected void finalize(){
        System.out.println("已回收");
    }
}
 public class M {
     public static void main(String[] args) throws IOException {
         N n = new N();
          n=null;//当n没有指向任何对象时,就会被垃圾回收器回收;至于jvm什么时间回收是不确定的;
         System.gc();//手动调一下回收;
         System.in.read();
     }

}

由于强引用是普遍的,所以reference是一个抽象类;

get()-----拿到引用
clear()方法----清除引用
enqueue()—清除引用,如果有队列的话则将其加入到队列中,返回值为boolean
isEnqueued()----检查是否被清除过,如果在引用创建前没有在队列中注册----即没有调用enqueue方法(或者引用创建时的构造方法参数无RQ),那么永远返回false;
代码1----->

   ReferenceQueue RQ= new ReferenceQueue();
     SoftReferencesoftReference= new SoftReference<>(new byte[5*1024*1024],RQ);
        SoftReferencesoftReference2= new SoftReference<>(new byte[5*1024*1024],RQ);
        SoftReferencesoftReference3= new SoftReference<>(new byte[5*1024*1024],RQ);
        System.out.println(softReference.get());//拿到引用
      
       // softReference.enqueue();//清除引用并将其放入队列中
         softReference.clear();//清除引用
        System.out.println(softReference.isEnqueued());//检查是否被enqueue过   enqueue()方法 得到 true,clear()得到false
        System.out.println(softReference.get());

reachabilityFence​(Object ref)

确保给定引用所引用的对象保持不变 强可及 , 无论程序之前的任何可能导致 变得无法访问的对象; 因此,引用的对象不是 至少在调用之后可以通过垃圾收集回收 这种方法。 调用此方法本身不会启动垃圾 收集或完成。
代码2----->

public class SoftReferenceDemo {
    public static void main(String[] args) throws InterruptedException {
        //设置堆内存大小为20M
ReferenceQueue RQ= new ReferenceQueue();
SoftReference softReference= new SoftReference<>(new byte[10*1024*1024]);
        Reference.reachabilityFence(softReference);
        System.gc();
        Thread.sleep(500);
        byte []bytes=new byte[15*1024*1024];
        System.out.println(softReference.get());
    }
}

报虚拟机OOM错误
如果注释掉Reference.reachabilityFence(softReference);
则会正常输出—
所以,该方法可以保持指定引用暂时不被虚拟机gc清除;

public static void reachabilityFence​(Object ref)方法中没有定义任何方法体,而是通过@ForceInline注解的方式来完成的----即强制内联;

简单的的说就是new的一个对象大部分都是强引用;

强引用的特点:
1,使用最普遍的引用;
2,当内存空间不足时,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。
如果强引用对象不使用时,需要弱化从而使GC能够回收,如下:
strongReference=null; 显示的设置为NULL
或让其超出对象的生命周期范围,则gc认为该对象不存在引用,这时就可以回收这个对象。具体什么时候收集这要取决于GC算法。

特点2演示:
代码4---->

public class SoftReferenceDemo {
    public static void main(String[] args)  {
byte[]bytes= new byte[1024*1024*10];
        System.out.println(bytes);
byte[]byte2= new byte[1024*1024*15];
        System.out.println(byte2);
    }
}

宁愿抛错误也不愿意回收掉还有引用指向的强引用;
(如果你的JVM没有抛出错误,那说明你的JVM分配的对空间足够大;)

如何设置堆空间最大最小值—(临时的,并非全局)
在 VM optionss中设置 -Xms20M -Xmx20M

引用计数:在Java堆中,每一个对象都会有一个引用计数属性,用来记录引用次数,每引用一次,计数加1,每释放1次引用,则计数减1。

在一个方法的内部有一个强引用,即-局部变量为强引用类型,这个引用(地址)保存在Java栈中,而真正的引用内容(Object)则保存在Java堆中。
当这个方法运行完成后,该强引用就会退出方法栈,引用计数就会回归到0,则引用当引用计数回归到0时,这个对象会被垃圾回收器回收。

但是如果这个强引用是全局变量时,就需要在不用这个对象时赋值为null,因为强引用不会被垃圾回收器回收,见代码3

软引用----->>>

当堆内存空间足够时,即使主动调用GC也不会立即回收它,而是通知垃圾回收器,将来给我回收了,具体什么时间回收------>>>当堆内存空间不够的时候才会回收该对象;因此软引用常用来做缓存用;
代码5---->

import java.lang.ref.SoftReference;
import java.util.concurrent.TimeUnit;

public class SoftReferenceDemo {
    public static void main(String[] args) throws InterruptedException {
        //引用了一个10M的字节数组
        SoftReferencesoftReference= new SoftReference<>(new byte[1024*1024*10]);
        //回收前打印一下这个引用
        System.out.println(softReference.get());
        //回收
        System.gc();
        //Thread.sleep(100);
        TimeUnit.SECONDS.sleep(1);
        //回收之后再打印一下这个引用
        System.out.println(softReference.get());
        //之后又来了一个引用,大小为15M
        SoftReferencesoftReference1= new SoftReference<>(new byte[1024*1024*15]);
       //强引用也可以,只要使得堆空间不够用就可以;
        //byte b[]=new byte[1024*1024*15];
        //再打印一下
        System.out.println(softReference.get());//null

    }
}

软引用的API------>>>

类的继承实现关系

public class SoftReference extends Reference

类描述:
软引用对象,由垃圾自行清除 收集器响应内存需求。 软引用是最常用的 实现内存敏感的缓存。

假设垃圾收集器在某个时间点确定对象是软可达的。 此时,它可以选择以原子方式清除对该对象的所有软引用,以及对通过强引用链可访问该对象的任何其他软可访问对象的所有软引用。在同一时间或稍后的时间,它将把那些新清除的、已注册到引用队列中的软引用放入队列。

对软可达对象的所有软引用保证在虚拟机抛出OutOfMemoryError 之前清除。
否则,对软引用被清除的时间或对不同对象的一组这样的引用被清除的顺序没有任何限制。 但是,鼓励虚拟机实现偏向于清除最近创建或最近使用的软引用。

简单的理解就是-----软引用在虚拟机抛出OOM的之前的时候一定会被清理;如果没有达到虚拟机内存域值,则对域软引用的清除不做任何操做;

构造方法—>

两个构造方法的区别在于一个是注册在队列中,一个没有注册在队列中;

代码6------->>>

  String str="HelloWorld";
        ReferenceQueue rq= new ReferenceQueue();
     SoftReferencereference=new SoftReference<>(str,rq);
        String s = reference.get();//拿到这个字符串
        System.out.println(s);
        str=null;
        System.gc();//通知回收一下,看看能不能被回收
        System.out.println(reference.get());


        System.out.println("------------------芬贝格县---------------");

        SoftReferencereference2=new SoftReference<>(str);
        String str1 = reference.get();//拿到这个字符串
        System.out.println(str1);
        str=null;
        System.gc();//通知回收一下,看看能不能被回收
        System.out.println(reference.get());

可以看到当 JVM内存够用时不会主动清除

get方法的源码----->>>

软引用在JVM内存不够用时的回收----->>>
代码7---->

public class SoftReferenceDemo {
    public static void main(String[] args) throws InterruptedException {
     ReferenceQueue RQ= new ReferenceQueue();
     SoftReferencesoftReference= new SoftReference<>(new byte[8*1024*1024]);
        System.out.println(softReference.get());
        SoftReferencesoftReference2= new SoftReference<>(new byte[8*1024*1024]);
        System.out.println(softReference2.get());
        System.gc();
        Thread.sleep(10000);
       byte []bytes2= new byte[5*1024*1024];
        System.out.println(softReference.get());
        System.out.println(softReference2.get());
    }
}

运行结果---->
有时候是这个结果,

有时候是另一个结果
虽然虚拟机鼓励回收实现偏向于清除最近创建或最近使用的软引用—试验了一下这句话的意思是说相对于最新一个创建的软引用而言最近的一个,但是并不保证其他的一定不会被回收!-------可以这么理解–保留最近的一个;
当把软引用数量提升到三个的时候

可以发现依然会出现都被清除的情况,说明即使是鼓励,但也不见得一定不会被回收;

再来看这个引用队列------>>>
代码8----->>>>>

public class SoftReferenceDemo {
    public static void main(String[] args) throws InterruptedException {
     ReferenceQueue RQ= new ReferenceQueue();
     SoftReferencesoftReference= new SoftReference<>(new byte[5*1024*1024],RQ);
        SoftReferencesoftReference2= new SoftReference<>(new byte[5*1024*1024],RQ);
        SoftReferencesoftReference3= new SoftReference<>(new byte[5*1024*1024],RQ);
        System.out.println(softReference.get());
        System.out.println(softReference2.get());
        System.out.println(softReference3.get());
        
        Reference poll1 = RQ.poll();
        System.out.println(poll1);//null
        System.gc();
        Thread.sleep(5000);
       byte []bytes2= new byte[8*1024*1024];
        System.out.println(softReference.get());
        System.out.println(softReference2.get());
        System.out.println(softReference3.get());

        Reference poll = RQ.poll();//gc之后软引用会被添加到队列中,如果队列中由可用软引用,则立即返回该引用,并将其从队列中删除
        System.out.println(poll);
    }
}

在ReferenceQueue中的三个方法---->>>
public Reference poll()

Polls this queue to see if a reference object is available. If one is available without further delay then it is removed from the queue and returned. Otherwise this method immediately returns .null

在SoftReference​(T referent, ReferenceQueue q)构造时,个在gc之后,软引用的对象会被添加到该队列q中;

如果在gc()调用之前,poll一定返回null;


代码9------>

public class SoftReferenceDemo {
    public static void main(String[] args) throws InterruptedException {
     ReferenceQueue RQ= new ReferenceQueue();
     SoftReferencesoftReference= new SoftReference<>(new byte[5*1024*1024],RQ);
        SoftReferencesoftReference2= new SoftReference<>(new byte[5*1024*1024],RQ);
        SoftReferencesoftReference3= new SoftReference<>(new byte[5*1024*1024],RQ);
        System.out.println(softReference.get());
        System.out.println(softReference2.get());
        System.out.println(softReference3.get());
      /*由于此时还没有gc(),所以为null*/
       
        System.gc();
        Thread.sleep(5000);
       byte []bytes2= new byte[8*1024*1024];
        System.out.println("--------------");
        System.out.println(softReference.get());
        System.out.println(softReference2.get());
        System.out.println(softReference3.get());

      
        Reference remove1 = RQ.remove();
        System.out.println(remove1);
    }
}

remove方法源码---->>

弱引用----->>>

代码10---->

import java.lang.ref.WeakReference;
class A {
    @Override
    protected void finalize() {
        System.out.println("finalize--invoke");
    }
}
public class WeakReferenceDemo {
    public static void main(String[] args) throws InterruptedException {

        WeakReference weak = new WeakReference<>(new A());
        System.out.println(weak.get());
        Thread.sleep(200);
        System.gc();
        System.out.println(weak.get());
    }
}

结果------->
只要gc看到了弱引用,直接就会回收弱引用;

继承实现关系----
public class WeakReference extends Reference

构造方法跟软引用类似;

根据弱引用的特点-----只要gc看到了弱引用,直接就会回收弱引用;

弱引用的API

WeakReference​(T referent)
创建一个引用给定对象的新弱引用。
WeakReference​(T referent, ReferenceQueue q)
创建一个引用给定对象的新弱引用,并且是 在给定的队列中注册。

弱引用在在ThreadLocal类中的应用
public class WeakReferenceDemo {
    public static void main(String[] args) throws InterruptedException, IOException {
        ThreadLocaltl= new ThreadLocal<>();
        tl.set(new A());
        tl.remove(); 
    }
}

set方法出打断点----->>

内存泄露-------意思是指内存中有一个永远不会被回收的对象,但是这个对象没有被其他地方使用,垃圾回收器看它是强引用类型也
不会主动去回收它,于是就会产生内存泄漏;

通过ThreadLocal类中的set(),我们来分析一下内存泄露的可能性!!

1,假使ThreadLocal中的静态内部类entry不是一个弱引用,而是一个强引用类型;
当ThreadLocal对象 指向null的时候,按理说整个tl对象应该被GC回收,但是由于ThreadLocal中的Entry[]指向一个强引用对象,那么这个ThreadLocal永远也不会被回收,虽然用不着,但是也不能被回收,所以就会产生内存泄漏;
2,同理如果是软引用类型,也可能产生内存泄露(在内存足够的情况下)
3,只有弱引用才是合适的;

内存溢出—是指内存不够用了,才会造成内存溢出即 OOM

注意:如果一个对象是偶尔(很少)的使用,并且希望在使用时随时就能获取到,但又不想影响此对象的垃圾收集,那么你应该用Weak Reference来记住此对象。

弱引用的生命周期---->>

WeakReference对象的生命周期基本由垃圾回收器决定,一旦垃圾回收线程发现了弱引用对象,在下一次GC过程中就会对其进行回收。

虚引用
public class PhrReferenceDemo {
    public static void main(String[] args) {
        ReferenceQueue rq = new ReferenceQueue();
        PhantomReference phantomReference = new PhantomReference<>(new byte[10 * 1024 * 1024], rq);
        System.out.println(phantomReference.get());//得到值为空--虚引用直接get是get不到的.
        //Reference.reachabilityFence(rq);
        phantomReference.enqueue();//移除引用并加入到队列中

        if (rq.poll() != null) {
            //说明有虚引用进入了队列,
            System.out.println("可以被回收");
            System.gc();
        }
        byte[] bytes = new byte[8 * 1024 * 1024];

        // phantomReference.enqueue();//移除并加入到队列中,并立即返回该移除对象
        //  System.out.println(phantomReference.isEnqueued());

        System.out.println(phantomReference.get());//得到值为空--虚引用直接get是get不到的.

    }
}
虚引用API

继承实现关系---->

public class PhantomReference extends Reference
Phantom 引用对象,在收集器之后排队 确定他们的参照物可能会被回收。 幻影 引用最常用于安排事后清理操作。

构造方法----->>可以看到只有一种构造,必须要加一个引用队列

虚引用-----当你get值得时候get不到,那虚引用还有必要存在吗?

虚引用主要用来跟踪对象被垃圾回收器回收的活动,一般是编写JVM虚拟机得时候用的,虚引用可以指向任何对象,

在清理堆外内存的时候可能会用到,那如何清理堆外内存呢?
可以通过一些方法使得虚引用进入队列,然后通过判断队列是否有虚引用来对一些情况进行处理;

小结------>>>>

我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号