精华内容
下载资源
问答
  • 最好的C++教程_哔哩哔哩 (゜-゜)つロ ...引用是对指针的扩展,所以一定要理解指针是怎么用的,原理是什么,才能更好地理解引用。虽说如此,在语法上,引用和指针还是有区别。我们可以创建一个指针变量,将它赋值为...
    最好的C++教程_哔哩哔哩 (゜-゜)つロ 干杯~-bilibiliwww.bilibili.com
    ce45832daaf91531bc476e68700c855a.png

    开始讲引用。一定要先看指针再看引用,因为本质上它们就是一回事,而引用不过是指针的syntax suger(这比喻),让指针更好用一点而已。引用是对指针的扩展,所以一定要理解指针是怎么用的,原理是什么,才能更好地理解引用。

    虽说如此,在语法上,引用和指针还是有区别。我们可以创建一个指针变量,将它赋值为NULL,之后再改变它的值。但是引用不行。引用必须指向一个现有的变量,并且引用自己本身并不是新的变量,它们不占有内存,只是对变量的引用。

    #include <iostream>
    #include "log.h"
    
    int main() {
    	int a = 5;
    
    	int& ref = a;
    
    	std::cin.get();
    }

    上面的代码中,紧跟在数据类型int之后的&就指明ref是一个引用,同时给ref赋值。这里注意区分,如果&用在变量前面,譬如 int*b = &a;那么此时&是用来取地址的;但是这里int&是说明引用。

    如果不给ref赋值,那么语法出错。

    a9e94be432492f3f2606f8a9e46bb8fa.png

    这里的ref相当于是变量a的假名。强调一下,并没有两个变量a和ref,只有一个变量a,以及对变量a的引用ref。

    #include <iostream>
    #include "log.h"
    
    int main() {
    
    	int a = 5;
    
    	int& ref = a;
    
    	ref = 2;
    
    	log(a);
    
    	std::cin.get();
    }
    

    此时的结果a的值就成了2。

    看下面这段代码:

    #include <iostream>
    #include "log.h"
    
    void  Increment(int value) {
    	value++;
    }
    
    int main() {
    
    	int a = 5;
    
    	int& ref = a;
    	Increment(a);
    
    	Log(a);
    	std::cin.get();
    }

    这个代码,大家应该都知道问题之所在,Increment此时是形参,所以不会对main()中的变量a产生任何影响。所以,一般来说可以使用指针或者引用来解决这个问题。形参相当于是传递了一个值过去,在子函数中相当于有一个局部变量,操作在这个局部变量上进行;而使用指针,是直接对这个指针所指向的内存位置进行操作,所以会影响main()中的变量。

    使用指针的版本:

    #include <iostream>
    #include "log.h"
    
    void  Increment(int* value) {
    	(*value)++;
    }
    
    int main() {
    
    	int a = 5;
    
    	int& ref = a;
    	Increment(&a);
    
    	Log(a);
    	std::cin.get();
    }

    这里需要注意的是,(*value)++,++的优先级貌似比*要高,所以必须加()。

    如果使用引用,代码会更简洁:

    #include <iostream>
    #include "log.h"
    
    void  Increment(int& value) {
    	value++;
    }
    
    int main() {
    
    	int a = 5;
    
    	int& ref = a;
    	Increment(a);
    
    	Log(a);
    	std::cin.get();
    }
    

    从上面的例子中可以看出,引用就是对指针的一点小改进,用起来更方便。所以,所以对指针不能进行的操作肯定也不能对引用进行。另外,ref不能进行修改。一旦绑定了ref,之后不能修改。这也是和指针不同的地方。

    include <iostream>
    #include "log.h"
    
    void  Increment(int& value) {
    	value++;
    }
    
    int main() {
    
    	int a = 5;
    	int b = 8;
    	int& ref = a;
    	
    	ref = b;
    	Log(a);
    	ref = 9;
    	Log(a);
            Log(b);
    	std::cin.get();
    }
    

    运行结果:

    894ccdd33590788db41940c6c38b3f4b.png

    上面代码的含义,ref = b;这一句不是将ref重新指向了b,而是将b的值8赋给了a。因此,后面的ref = 9;也只对a有影响,而不会对b有影响。

    展开全文
  • 年前看到一个关于软引用引用相关的面试题,自己之前也就听说过弱引用的概念。所以最近把相关ref包下的源码看了下,也找了点资料,把这部分内容总结一下,如下是这...为什么会出现这个问题,怎么解决呢,今天这个内...

    年前看到一个关于软引用弱引用相关的面试题,自己之前也就听说过弱引用的概念。所以最近把相关ref包下的源码看了下,也找了点资料,把这部分内容总结一下,如下是这篇博客的大纲

    一.引入

    二.几种引用的概念和使用

    三.几种引用的使用场景和注意事项

    四.JDK中的ReferenceQueue

    五.总结



    一.引入

    在java中,我们肯定会碰到OOM(OutOfMemory)问题。为什么会出现这个问题,怎么解决呢,今天这个内容就给出了一种方案来说明,解决


    二.几种引用的概念和使用

    从JDK1.2之前的版本中,若一个对象不被任何变量引用,那么程序就无法再使用这个对象,也就是说,只有对象时可触及状态(reachable),程序

    才能使用它,从1.2开始,把对象的引用分为四个级别,由高到低依次是:强引用,软引用,弱引用,虚引用。从而使程序能够更灵活地控制对象

    的生命周期(1.可以通过代码的方式决定某些对象的生命周期;2.有利于JVM进行垃圾回收)。关于引用相关的类都在java.lang.ref包下,如下图

    所示:



    2.1强引用(StrongReference)

    强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机

    宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。

    强引用在日常代码中普遍存在,比如下面代码test方法内部有三个强引用list,str,objArray,这三个引用保存在栈中

    而真正的应用内存(new出的对象)保存在堆中。

    public void test() {
    	List<String> list = new ArrayList<String>();
    	String str = "S1amDuncan";
    	Object[] abjArray = new Object[10000];
    }

    只要对象有强引用与之关联,那么JVM就不会回收这个对象,比如第三个objArray,

    JVM宁愿抛出OutOfMemoryError也不会回收这个对象。不过需要注意的是当test方法使用完后,list,str,objArray就不存在了,

    所以他们指向的对象会被JVM回收。如果想中断强引用和某个对象之间的关联,可以显示的将引用赋值为null,这样,

    JVM就会在合适的时间回收该对象,这个在JDK源码中有很多地方应用到了,

    我们平时在阅读JDK源码的时候可能会看到如下代码注释,就是这样做的。

    // Let gc do its work


    2.2软引用(SoftReference)

    如果一个对象只具有软引用,如果内存空间足够,垃圾回收器就不会回收它,如果内存不够了,就会回收这些对象的内存,只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可以用来实现内存敏感的高速缓存。软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引起的对象被垃圾回收期回收,Java虚拟机就会把这个软引用加入到与之相关联的引用队列中。使用示例如下

    public class TestSoft {
        public static void main(String[] args) {        
            SoftReference<String> sr = new SoftReference<String>(new String("hello"));
            System.out.println(sr.get());
        }
    }

    2.3弱引用(WeakReference)

    弱引用与软引用的区别在于:弱引用的对象(仅持有弱引用)拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,无论当前内存空间是否足够,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。弱引用可以和一个引用队列(ReferenmceQueue)联合使用,如果弱引用所引起的对象被垃圾回收器回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。使用示例如下

    public class TestWeak {
        public static void main(String[] args) {     
            WeakReference<String> wr = new WeakReference<String>(new String("hello"));        
            System.out.println(wr.get());
            System.gc();                //通知JVM的gc进行垃圾回收
            System.out.println(wr.get());
        }
    }

    2.4虚引用(PhantomReference)

    虚引用,顾名思义,就是形同虚设,与其他几种引用不同,虚引用并不会决定对象的声明周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别就在于:虚引用必须和引用队列(ReferenceQueue)联合使用,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以再所引用的对象的内存被回收之前采取必要的。使用示例如下

    public class TestPhantom {
        public static void main(String[] args) {
            ReferenceQueue<String> queue = new ReferenceQueue<String>();
            PhantomReference<String> pr = new PhantomReference<String>(new String("hello"), queue);
            System.out.println(pr.get());
        }
    }

    以上内容需要注意这个概念,一个对象并不是只能持有一种类型的引用,可能强软弱虚引用都有。


    三.几种引用的使用场景和注意事项

    3.1强引用

    关于强引用的使用场景就不说了,基本日常工作中都是用到的这个。


    3.2软引用

    软引用可以用来实现内存敏感的高速缓存,例如浏览器的后退按钮,按后退时,这个后退需要显示的网页内容是重新进行请求还是从缓存中取出呢,这就要看具体的实现策略了。

    (1) 如果一个网页在浏览结束时就进行内容的回收,那么按后退查看前面浏览页面的时候又需要全部重新请求

    (2)如果将浏览过的网页存储到内存中就会造成内存的大量浪费,甚至造成内存溢出

    这个时候就可以使用软引用

    Browser prev = new Browser();               // 获取页面进行浏览
    SoftReference sr = new SoftReference(prev); // 浏览完毕后置为软引用    
    if(sr.get()!=null){ 
        rev = (Browser) sr.get();           // 还没有被回收器回收,直接获取
    }else{
        prev = new Browser();               // 由于内存吃紧,所以对软引用的对象回收了
        sr = new SoftReference(prev);       // 重新构建
    }
    这样就能很好的解决实际问题。

    3.3弱引用

    当你想引用一个对象,但是这个对象有自己的生命周期,你不想介入这个对象的生命周期,这个时候就可以使用弱引用,这个引用不会在对象的垃圾回收判断中产生任何附加的影响

    如下

    public class ReferenceTest {
    
        private static ReferenceQueue<VeryBig> rq = new ReferenceQueue<VeryBig>();
    
        public static void checkQueue() {
            Reference<? extends VeryBig> ref = null;
            while ((ref = rq.poll()) != null) {
                if (ref != null) {
                    System.out.println("In queue: "    + ((VeryBigWeakReference) (ref)).id);
                }
            }
        }
    
        public static void main(String args[]) {
            int size = 3;
            LinkedList<WeakReference<VeryBig>> weakList = new LinkedList<WeakReference<VeryBig>>();
            for (int i = 0; i < size; i++) {
                weakList.add(new VeryBigWeakReference(new VeryBig("Weak " + i), rq));
                System.out.println("Just created weak: " + weakList.getLast());
    
            }
    
            System.gc(); 
            try { // 下面休息几分钟,让上面的垃圾回收线程运行完成
                Thread.currentThread().sleep(6000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            checkQueue();
        }
    }
    
    class VeryBig {
        public String id;
        // 占用空间,让线程进行回收
        byte[] b = new byte[2 * 1024];
    
        public VeryBig(String id) {
            this.id = id;
        }
    
        protected void finalize() {
            System.out.println("Finalizing VeryBig " + id);
        }
    }
    
    class VeryBigWeakReference extends WeakReference<VeryBig> {
        public String id;
    
        public VeryBigWeakReference(VeryBig big, ReferenceQueue<VeryBig> rq) {
            super(big, rq);
            this.id = big.id;
        }
    
        protected void finalize() {
            System.out.println("Finalizing VeryBigWeakReference " + id);
        }
    }
    输出结果为:
    Just created weak: com.javabase.reference.VeryBigWeakReference@1641c0
    Just created weak: com.javabase.reference.VeryBigWeakReference@136ab79
    Just created weak: com.javabase.reference.VeryBigWeakReference@33c1aa
    Finalizing VeryBig Weak 2
    Finalizing VeryBig Weak 1
    Finalizing VeryBig Weak 0
    In queue: Weak 1
    In queue: Weak 2
    In queue: Weak 0

    3.4虚引用

    这个在实际中基本很少使用,清楚2.4相关的概念即可。


    四.JDK中的ReferenceQueue

    在上面中多次提到了ReferenceQueue,可能对这个类的概念还不太清晰,那么接下来对ReferenceQueue做一个简单的讲解。

    举个栗子:你从超市买了各种小工具放在在房间不同的柜子里,每个柜子有一把钥匙。如果发现有的小工具很有用,你就一直留着,如果发现有的没用,就把这个丢到垃圾桶,由清洁工人收走,如果这个工具被扔到垃圾桶,想要再把它捡回来使用就不可能了。但是有时候,可能碰到这个工具虽然占着你柜子的空间,而且暂时没用,但是立刻扔掉也不划算,因为将来可能还会派上用场,对于这种可有可无的物品,一种方法就是,如果柜子空间足够,就先把它保留在柜子里,如果柜子里空间不够,再扔掉这些可有可无的物品。这个时候我们把这种可有可无需要暂时保留的物品(对象)对应柜子的钥匙(引用)放在一个包(引用队列)里。如果需要用到就去这个包里取,再打开对应的柜子看是否这个物品还在,如果不在就说明可能之前空间不足或者其他原因被人丢掉了。


    五.总结


    展开全文
  • 一文读懂java中的Reference引用类型简介java中...Reference是跟java垃圾回收机制息息相关的类,通过探讨Reference的实现可以更加深入的理解java的垃圾回收是怎么工作的。本文先从java中的四种引用类型开始,一步一...

    一文读懂java中的Reference和引用类型

    简介

    java中有值类型也有引用类型,引用类型一般是针对于java中对象来说的,今天介绍一下java中的引用类型。java为引用类型专门定义了一个类叫做Reference。Reference是跟java垃圾回收机制息息相关的类,通过探讨Reference的实现可以更加深入的理解java的垃圾回收是怎么工作的。

    本文先从java中的四种引用类型开始,一步一步揭开Reference的面纱。

    java中的四种引用类型分别是:强引用,软引用,弱引用和虚引用。

    强引用Strong Reference

    java中的引用默认就是强引用,任何一个对象的赋值操作就产生了对这个对象的强引用。

    我们看一个例子:

    public class StrongReferenceUsage {

    @Test

    public void stringReference(){

    Object obj = new Object();

    }

    }

    上面我们new了一个Object对象,并将其赋值给obj,这个obj就是new Object()的强引用。

    强引用的特性是只要有强引用存在,被引用的对象就不会被垃圾回收。

    软引用Soft Reference

    软引用在java中有个专门的SoftReference类型,软引用的意思是只有在内存不足的情况下,被引用的对象才会被回收。

    先看下SoftReference的定义:

    public class SoftReference extends Reference

    SoftReference继承自Reference。它有两种构造函数:

    public SoftReference(T referent)

    和:

    public SoftReference(T referent, ReferenceQueue super T> q)

    第一个参数很好理解,就是软引用的对象,第二个参数叫做ReferenceQueue,是用来存储封装的待回收Reference对象的,ReferenceQueue中的对象是由Reference类中的ReferenceHandler内部类进行处理的。

    我们举个SoftReference的例子:

    @Test

    public void softReference(){

    Object obj = new Object();

    SoftReference soft = new SoftReference<>(obj);

    obj = null;

    log.info("{}",soft.get());

    System.gc();

    log.info("{}",soft.get());

    }

    输出结果:

    22:50:43.733 [main] INFO com.flydean.SoftReferenceUsage - java.lang.Object@71bc1ae4

    22:50:43.749 [main] INFO com.flydean.SoftReferenceUsage - java.lang.Object@71bc1ae4

    可以看到在内存充足的情况下,SoftReference引用的对象是不会被回收的。

    弱引用weak Reference

    weakReference和softReference很类似,不同的是weekReference引用的对象只要垃圾回收执行,就会被回收,而不管是否内存不足。

    同样的WeakReference也有两个构造函数:

    public WeakReference(T referent);

    public WeakReference(T referent, ReferenceQueue super T> q);

    含义和SoftReference一致,这里就不再重复表述了。

    我们看下弱引用的例子:

    @Test

    public void weakReference() throws InterruptedException {

    Object obj = new Object();

    WeakReference weak = new WeakReference<>(obj);

    obj = null;

    log.info("{}",weak.get());

    System.gc();

    log.info("{}",weak.get());

    }

    输出结果:

    22:58:02.019 [main] INFO com.flydean.WeakReferenceUsage - java.lang.Object@71bc1ae4

    22:58:02.047 [main] INFO com.flydean.WeakReferenceUsage - null

    我们看到gc过后,弱引用的对象被回收掉了。

    虚引用PhantomReference

    PhantomReference的作用是跟踪垃圾回收器收集对象的活动,在GC的过程中,如果发现有PhantomReference,GC则会将引用放到ReferenceQueue中,由程序员自己处理,当程序员调用ReferenceQueue.pull()方法,将引用出ReferenceQueue移除之后,Reference对象会变成Inactive状态,意味着被引用的对象可以被回收了。

    和SoftReference和WeakReference不同的是,PhantomReference只有一个构造函数,必须传入ReferenceQueue:

    public PhantomReference(T referent, ReferenceQueue super T> q)

    看一个PhantomReference的例子:

    @Slf4j

    public class PhantomReferenceUsage {

    @Test

    public void usePhantomReference(){

    ReferenceQueue rq = new ReferenceQueue<>();

    Object obj = new Object();

    PhantomReference phantomReference = new PhantomReference<>(obj,rq);

    obj = null;

    log.info("{}",phantomReference.get());

    System.gc();

    Reference r = (Reference)rq.poll();

    log.info("{}",r);

    }

    }

    运行结果:

    07:06:46.336 [main] INFO com.flydean.PhantomReferenceUsage - null

    07:06:46.353 [main] INFO com.flydean.PhantomReferenceUsage - java.lang.ref.PhantomReference@136432db

    我们看到get的值是null,而GC过后,poll是有值的。

    因为PhantomReference引用的是需要被垃圾回收的对象,所以在类的定义中,get一直都是返回null:

    public T get() {

    return null;

    }

    Reference和ReferenceQueue

    讲完上面的四种引用,接下来我们谈一下他们的父类Reference和ReferenceQueue的作用。

    Reference是一个抽象类,每个Reference都有一个指向的对象,在Reference中有5个非常重要的属性:referent,next,discovered,pending,queue。

    private T referent; /* Treated specially by GC */

    volatile ReferenceQueue super T> queue;

    Reference next;

    transient private Reference discovered; /* used by VM */

    private static Reference pending = null;

    每个Reference都可以看成是一个节点,多个Reference通过next,discovered和pending这三个属性进行关联。

    先用一张图来对Reference有个整体的概念:

    948d9818108db9d148bbbbbd5eed64b3.png

    referent就是Reference实际引用的对象。

    通过next属性,可以构建ReferenceQueue。

    通过discovered属性,可以构建Discovered List。

    通过pending属性,可以构建Pending List。

    四大状态

    在讲这三个Queue/List之前,我们先讲一下Reference的四个状态:

    d1df42c19fd97f1e89d6f00ed7a53655.png

    从上面的图中,我们可以看到一个Reference可以有四个状态。

    因为Reference有两个构造函数,一个带ReferenceQueue,一个不带。

    Reference(T referent) {

    this(referent, null);

    }

    Reference(T referent, ReferenceQueue super T> queue) {

    this.referent = referent;

    this.queue = (queue == null) ? ReferenceQueue.NULL : queue;

    }

    对于带ReferenceQueue的Reference,GC会把要回收对象的Reference放到ReferenceQueue中,后续该Reference需要程序员自己处理(调用poll方法)。

    不带ReferenceQueue的Reference,由GC自己处理,待回收的对象其Reference状态会变成Inactive。

    创建好了Reference,就进入active状态。

    active状态下,如果引用对象的可到达状态发送变化就会转变成Inactive或Pending状态。

    Inactive状态很好理解,到达Inactive状态的Reference状态不能被改变,会等待GC回收。

    Pending状态代表等待入Queue,Reference内部有个ReferenceHandler,会调用enqueue方法,将Pending对象入到Queue中。

    入Queue的对象,其状态就变成了Enqueued。

    Enqueued状态的对象,如果调用poll方法从ReferenceQueue拿出,则该Reference的状态就变成了Inactive,等待GC的回收。

    这就是Reference的一个完整的生命周期。

    三个Queue/List

    有了上面四个状态的概念,我们接下来讲三个Queue/List:ReferenceQueue,discovered List和pending List。

    ReferenceQueue在讲状态的时候已经讲过了,它本质是由Reference中的next连接而成的。用来存储GC待回收的对象。

    pending List就是待入ReferenceQueue的list。

    discovered List这个有点特别,在Pending状态时候,discovered List就等于pending List。

    在Active状态的时候,discovered List实际上维持的是一个引用链。通过这个引用链,我们可以获得引用的链式结构,当某个Reference状态不再是Active状态时,需要将这个Reference从discovered List中删除。

    WeakHashMap

    最后讲一下WeakHashMap,WeakHashMap跟WeakReference有点类似,在WeakHashMap如果key不再被使用,被赋值为null的时候,该key对应的Entry会自动从WeakHashMap中删除。

    我们举个例子:

    @Test

    public void useWeakHashMap(){

    WeakHashMap map = new WeakHashMap<>();

    Object key1= new Object();

    Object value1= new Object();

    Object key2= new Object();

    Object value2= new Object();

    map.put(key1, value1);

    map.put(key2, value2);

    log.info("{}",map);

    key1 = null;

    System.gc();

    log.info("{}",map);

    }

    输出结果:

    [main] INFO com.flydean.WeakHashMapUsage - {java.lang.Object@14899482=java.lang.Object@2437c6dc, java.lang.Object@11028347=java.lang.Object@1f89ab83}

    [main] INFO com.flydean.WeakHashMapUsage - {java.lang.Object@14899482=java.lang.Object@2437c6dc}

    可以看到gc过后,WeakHashMap只有一个Entry了。

    总结

    本文讲解了4个java中的引用类型,并深入探讨了Reference的内部机制,感兴趣的小伙伴可以留言一起讨论。

    欢迎关注我的公众号:程序那些事,更多精彩等着您!

    更多内容请访问 www.flydean.com

    展开全文
  • Reference是跟java垃圾回收机制息息相关的类,通过探讨Reference的实现可以更加深入的理解java的垃圾回收是怎么工作的。本文先从java中的四种引用类型开始,一步一步揭开Reference的面纱。java中的四种...

    简介

    java中有值类型也有引用类型,引用类型一般是针对于java中对象来说的,今天介绍一下java中的引用类型。java为引用类型专门定义了一个类叫做Reference。Reference是跟java垃圾回收机制息息相关的类,通过探讨Reference的实现可以更加深入的理解java的垃圾回收是怎么工作的。

    本文先从java中的四种引用类型开始,一步一步揭开Reference的面纱。

    java中的四种引用类型分别是:强引用,软引用,弱引用和虚引用。

    强引用Strong Reference

    java中的引用默认就是强引用,任何一个对象的赋值操作就产生了对这个对象的强引用。

    我们看一个例子:

    public class StrongReferenceUsage {

    @Test

    public void stringReference(){

    Object obj = new Object();

    }

    }

    上面我们new了一个Object对象,并将其赋值给obj,这个obj就是new Object()的强引用。

    强引用的特性是只要有强引用存在,被引用的对象就不会被垃圾回收。

    软引用Soft Reference

    软引用在java中有个专门的SoftReference类型,软引用的意思是只有在内存不足的情况下,被引用的对象才会被回收。

    先看下SoftReference的定义:

    public class SoftReference extends Reference

    SoftReference继承自Reference。它有两种构造函数:

    public SoftReference(T referent)

    和:

    public SoftReference(T referent, ReferenceQueue super T> q)

    第一个参数很好理解,就是软引用的对象,第二个参数叫做ReferenceQueue,是用来存储封装的待回收Reference对象的,ReferenceQueue中的对象是由Reference类中的ReferenceHandler内部类进行处理的。

    我们举个SoftReference的例子:

    @Test

    public void softReference(){

    Object obj = new Object();

    SoftReference soft = new SoftReference<>(obj);

    obj = null;

    log.info("{}",soft.get());

    System.gc();

    log.info("{}",soft.get());

    }

    输出结果:

    22:50:43.733 [main] INFO com.flydean.SoftReferenceUsage - java.lang.Object@71bc1ae4

    22:50:43.749 [main] INFO com.flydean.SoftReferenceUsage - java.lang.Object@71bc1ae4

    可以看到在内存充足的情况下,SoftReference引用的对象是不会被回收的。

    弱引用weak Reference

    weakReference和softReference很类似,不同的是weekReference引用的对象只要垃圾回收执行,就会被回收,而不管是否内存不足。

    同样的WeakReference也有两个构造函数:

    public WeakReference(T referent);

    public WeakReference(T referent, ReferenceQueue super T> q);

    含义和SoftReference一致,这里就不再重复表述了。

    我们看下弱引用的例子:

    @Test

    public void weakReference() throws InterruptedException {

    Object obj = new Object();

    WeakReference weak = new WeakReference<>(obj);

    obj = null;

    log.info("{}",weak.get());

    System.gc();

    log.info("{}",weak.get());

    }

    输出结果:

    22:58:02.019 [main] INFO com.flydean.WeakReferenceUsage - java.lang.Object@71bc1ae4

    22:58:02.047 [main] INFO com.flydean.WeakReferenceUsage - null

    我们看到gc过后,弱引用的对象被回收掉了。

    虚引用PhantomReference

    PhantomReference的作用是跟踪垃圾回收器收集对象的活动,在GC的过程中,如果发现有PhantomReference,GC则会将引用放到ReferenceQueue中,由程序员自己处理,当程序员调用ReferenceQueue.pull()方法,将引用出ReferenceQueue移除之后,Reference对象会变成Inactive状态,意味着被引用的对象可以被回收了。

    和SoftReference和WeakReference不同的是,PhantomReference只有一个构造函数,必须传入ReferenceQueue:

    public PhantomReference(T referent, ReferenceQueue super T> q)

    看一个PhantomReference的例子:

    @Slf4j

    public class PhantomReferenceUsage {

    @Test

    public void usePhantomReference(){

    ReferenceQueue rq = new ReferenceQueue<>();

    Object obj = new Object();

    PhantomReference phantomReference = new PhantomReference<>(obj,rq);

    obj = null;

    log.info("{}",phantomReference.get());

    System.gc();

    Reference r = (Reference)rq.poll();

    log.info("{}",r);

    }

    }

    运行结果:

    07:06:46.336 [main] INFO com.flydean.PhantomReferenceUsage - null

    07:06:46.353 [main] INFO com.flydean.PhantomReferenceUsage - java.lang.ref.PhantomReference@136432db

    我们看到get的值是null,而GC过后,poll是有值的。

    因为PhantomReference引用的是需要被垃圾回收的对象,所以在类的定义中,get一直都是返回null:

    public T get() {

    return null;

    }

    Reference和ReferenceQueue

    讲完上面的四种引用,接下来我们谈一下他们的父类Reference和ReferenceQueue的作用。

    Reference是一个抽象类,每个Reference都有一个指向的对象,在Reference中有5个非常重要的属性:referent,next,discovered,pending,queue。

    private T referent; /* Treated specially by GC */

    volatile ReferenceQueue super T> queue;

    Reference next;

    transient private Reference discovered; /* used by VM */

    private static Reference pending = null;

    每个Reference都可以看成是一个节点,多个Reference通过next,discovered和pending这三个属性进行关联。

    先用一张图来对Reference有个整体的概念:

    referent就是Reference实际引用的对象。

    通过next属性,可以构建ReferenceQueue。

    通过discovered属性,可以构建Discovered List。

    通过pending属性,可以构建Pending List。

    四大状态

    在讲这三个Queue/List之前,我们先讲一下Reference的四个状态:

    从上面的图中,我们可以看到一个Reference可以有四个状态。

    因为Reference有两个构造函数,一个带ReferenceQueue,一个不带。

    Reference(T referent) {

    this(referent, null);

    }

    Reference(T referent, ReferenceQueue super T> queue) {

    this.referent = referent;

    this.queue = (queue == null) ? ReferenceQueue.NULL : queue;

    }

    对于带ReferenceQueue的Reference,GC会把要回收对象的Reference放到ReferenceQueue中,后续该Reference需要程序员自己处理(调用poll方法)。

    不带ReferenceQueue的Reference,由GC自己处理,待回收的对象其Reference状态会变成Inactive。

    创建好了Reference,就进入active状态。

    active状态下,如果引用对象的可到达状态发送变化就会转变成Inactive或Pending状态。

    Inactive状态很好理解,到达Inactive状态的Reference状态不能被改变,会等待GC回收。

    Pending状态代表等待入Queue,Reference内部有个ReferenceHandler,会调用enqueue方法,将Pending对象入到Queue中。

    入Queue的对象,其状态就变成了Enqueued。

    Enqueued状态的对象,如果调用poll方法从ReferenceQueue拿出,则该Reference的状态就变成了Inactive,等待GC的回收。

    这就是Reference的一个完整的生命周期。

    三个Queue/List

    有了上面四个状态的概念,我们接下来讲三个Queue/List:ReferenceQueue,discovered List和pending List。

    ReferenceQueue在讲状态的时候已经讲过了,它本质是由Reference中的next连接而成的。用来存储GC待回收的对象。

    pending List就是待入ReferenceQueue的list。

    discovered List这个有点特别,在Pending状态时候,discovered List就等于pending List。

    在Active状态的时候,discovered List实际上维持的是一个引用链。通过这个引用链,我们可以获得引用的链式结构,当某个Reference状态不再是Active状态时,需要将这个Reference从discovered List中删除。

    WeakHashMap

    最后讲一下WeakHashMap,WeakHashMap跟WeakReference有点类似,在WeakHashMap如果key不再被使用,被赋值为null的时候,该key对应的Entry会自动从WeakHashMap中删除。

    我们举个例子:

    @Test

    public void useWeakHashMap(){

    WeakHashMap map = new WeakHashMap<>();

    Object key1= new Object();

    Object value1= new Object();

    Object key2= new Object();

    Object value2= new Object();

    map.put(key1, value1);

    map.put(key2, value2);

    log.info("{}",map);

    key1 = null;

    System.gc();

    log.info("{}",map);

    }

    输出结果:

    [main] INFO com.flydean.WeakHashMapUsage - {java.lang.Object@14899482=java.lang.Object@2437c6dc, java.lang.Object@11028347=java.lang.Object@1f89ab83}

    [main] INFO com.flydean.WeakHashMapUsage - {java.lang.Object@14899482=java.lang.Object@2437c6dc}

    可以看到gc过后,WeakHashMap只有一个Entry了。

    总结

    本文讲解了4个java中的引用类型,并深入探讨了Reference的内部机制,感兴趣的小伙伴可以留言一起讨论。欢迎关注我的公众号:程序那些事,更多精彩等着您!

    更多内容请访问一文读懂java中的Reference和引用类型​www.flydean.com2d0c4da433168d2267990179aaff2a85.png

    展开全文
  • http://blogs.msdn.com/b/jimgries/archive/2005/11/16/493431.aspx 转载于:https://www.cnblogs.com/livenn/archive/2010/12/22/1913935.html
  • Reference是跟java垃圾回收机制息息相关的类,通过探讨Reference的实现可以更加深入的理解java的垃圾回收是怎么工作的。本文先从java中的四种引用类型开始,一步一步揭开Reference的面纱。java中的四种...
  • 一文读懂java中的Reference引用类型

    千次阅读 2020-05-04 07:03:51
    Reference是跟java垃圾回收机制息息相关的类,通过探讨Reference的实现可以更加深入的理解java的垃圾回收是怎么工作的。 本文先从java中的四种引用类型开始,一步一步揭开Reference的面纱。 java中的四种引用类型...
  • Reference是跟java垃圾回收机制息息相关的类,通过探讨Reference的实现可以更加深入的理解java的垃圾回收是怎么工作的。本文先从java中的四种引用类型开始,一步一步揭开Reference的面纱。java中的四种引用...
  • 很多C++初学者搞不清楚C++引用类型,不知有什么用,所以也不知怎么用。一个问题令人迷惑,不是读者有问题,而是教科书有问题。 和多数初学者一样,笔者在初学C++时,关于引用类型,教科书上也是如下表所述: ...
  • Java虚拟机可以处理的类型有两种,一种是原始类型(Primitive Types),一种是引用类型(Reference Types).与之对应,也存在有原始值(Primitive Values)和引用值(Reference Values)两种类型的数值可用于变量赋值、参数...
  • dubbo:reference引入的bean,该怎么注入,可以用注解@Autowired注入吗,或者@reference注入,还是只能set注入??? <!-- 引用 dubbo 服务 --> <dubbo:reference id="sellerService" interface=...
  • Reference是跟java垃圾回收机制息息相关的类,通过探讨Reference的实现可以更加深入的理解java的垃圾回收是怎么工作的。本文先从java中的四种引用类型开始,一步一步揭开Reference的面纱。java中的四种...
  • 1.将对象(对象的引用)作为参数传递时传递的是引用(相当于指针)。也就是说函数内对参数所做的修改会影响原来的对象。 2.当将基本类型或基本类型的包装集作为参数传递时,传递的是值。也就是说函数内对参数所做的...
  • Reference是跟java垃圾回收机制息息相关的类,通过探讨Reference的实现可以更加深入的理解java的垃圾回收是怎么工作的。本文先从java中的四种引用类型开始,一步一步揭开Reference的面纱。java中的四种...
  • Reference是跟java垃圾回收机制息息相关的类,通过探讨Reference的实现可以更加深入的理解java的垃圾回收是怎么工作的。本文先从java中的四种引用类型开始,一步一步揭开Reference的面纱。java中的四种...
  • Android空引用问题的解决方法——on a null object reference前言:今天写了一个小程序,自定义view组件,但是却怎么也运行不起来,一直空指针引用报错。 java.lang.NullPointerException: Attempt to invoke ...
  • 一、什么是“设计的多实例引用” 在层次化的设计中,我们经常会将一个module例化多份,这些例化的instance就是设计的module的多实例引用。 二、为什么要解决多实例引用 ...三、怎么解决多实例引用 ...
  • 背景: 使用.bib文件管理参考文献,希望Reference部分按照引用顺序排序,正文中引用格式为xx[n]。 如图: 正文显示: Reference部分: 方法: 首先导入包 \usepackage[numbers]{natbib} 然后先用\citeauthor{}...
  • 关于引用计数法,我们可以先看一段wiki上的描述:As a collection algorithm, reference counting tracks, for each object, a count of the number of references to it held by other objects. If an object's ...
  • 《java编程思想》关于引用计数法,我们可以先看一段wiki上的描述:As a collection algorithm, reference counting tracks, for each object, a count of the number of references to it held by o...
  • <PackageReference Include="Newtonsoft.Json" Version="9.0.1"> <ExcludeAssets>...在项目文件里面,这一行代表引用包,可是有三个子项目,需要说一下:IncludeAssetsExcludeAssetsP...
  • 具体怎么引用有很多大神都发了教程,跟着做就好。 我这里说一下大神们没想到(人会这么蠢)的部分吧。 如果你跟着大神们一步步这样做下来结果没有出结果,然后还出现了和我一样的问题,那原因可能就是,你qt版本不对...
  • 就我遇到的情况而言,我写了两个c#代码文件绑定到了两个不同的gameobject上,结果导致了一个代码文件里的A类要引用另一个代码文件的里的B类,然而B类还没被unity加载,所以A类找不到B类,就报错了。所以把它们两个...
  • AtomicReferenceAn object reference that may be updated atomically.The AtomicReference class provides reference objects that may be read and written atomically, so when multiple threads try to reach th...
  • 我们只需要在csc的参数中加上/reference:"*.exe"就行了,可是如果要在VS.NET中引用同样的exe文件并编译,居然默认是不行的! 比如我们在VS.NET 2003里面引用一个叫HelloWorld.exe的托管可执行文件,会得到如下的...
  • Regex : 本文主要讲述正则表达式中的捕获组(Capturing Group)的概念本文的正则表达式在 Java 中测试(需要注意的是这里的...简单的理解就是把正则表达式中的某部分用 () 括起来表示为一个组,纯理论解释不怎么
  • 我一直在写关于JavaScript的文章,我也帮助一些朋友...例如,在C#中,ref关键字用于定义具有其中一个参数的方法,该参数遵循call by reference的调用方式。public void useCallByRef(ref int val)在深入讨论主题...
  • 引用主要分为 :强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(PhantomReference) 四种,引用的强度依次骤减。强引用:就是指在代码之中普遍存在的,类似:“Object objectRef...

空空如也

空空如也

1 2 3 4 5 ... 18
收藏数 353
精华内容 141
关键字:

怎么引用reference