精华内容
下载资源
问答
  • 强引用,软引用和弱引用的区别
    2022-07-20 13:50:38

    各个引用的区别

    强引用: 强引用指的是通过new对象创建的引用,垃圾回收器即使是内存不足也不会回收强引用指向的对象。

    软引用: 软引用是通过SoftRefrence实现的,它的生命周期比强引用短,在内存不足,抛出OOM之前,垃圾回收器会回收软引用引用的对象。软引用常见的使用场景是存储一些内存敏感的缓存,只有当内存不足时会被回收。

    弱引用: 弱引用是通过WeakRefrence实现的,它的生命周期比软引用还短,GC只要扫描到弱引用的对象就会回收。弱引用常见的使用场景也是存储一些内存敏感的缓存。

    虚引用: 虚引用是通过PhantomRefrence实现的,它的生命周期最短,随时可能被回收。如果一个对象只被虚引用引用,我们无法通过虚引用来访问这个对象的任何属性和方法。get()返回的都是null,它的作用仅仅是保证对象在finalize后,做某些事情。虚引用常见的使用场景是跟踪对象被垃圾回收的活动,当一个虚引用关联的对象被垃圾回收器回收之前会收到一条系统通知。

    实验来验证:

    package com.figo.test;
    
    import lombok.Getter;
    import lombok.Setter;
    import org.junit.jupiter.api.Test;
    
    import java.lang.ref.PhantomReference;
    import java.lang.ref.ReferenceQueue;
    import java.lang.ref.WeakReference;
    import java.util.LinkedList;
    import java.util.List;
    
    /**
     * @ClassName:ReferenceTest
     * @PackageName:com.figo.test
     * @Description:类描述
     * @Date:2022/7/20 10:49
     * @Author:figo
     */
    public class ReferenceTest {
        private ReferenceQueue<? super Employee> referenceQueue;
    
        public class Employee
        {
            @Getter
            @Setter
            String name="andy";
    
            @Getter
            @Setter
            int age=100;
        }
        @Test
        public void testReference(){
            //new出来是强引用
            Employee employee = new Employee();//调用无参构造函数,自动为相关属性赋值
            //软引用,在内存没有出现oom时不会被回收,不能和WeakReference一起使用,否则WeakReference失效
            //SoftReference<Employee> employeeSoftReference = new SoftReference<>(employee);
            //弱引用
            WeakReference<Employee> employeeWeakReference=new WeakReference<>(employee);
            //虚引用
            referenceQueue=new ReferenceQueue<>();
            PhantomReference<Employee> employeePhantomReference=new PhantomReference<>(employee, referenceQueue);
    
            System.out.println("------Before Gc------");
            //System.out.println("软引用对象是否还在="+employeeSoftReference.get());
            System.out.println("弱引用对象是否还在="+employeeWeakReference.get());
            System.out.println("虚引用对象是否还在="+employeePhantomReference.get());
    
            employee=null;//被引用对象清空
            System.gc();//进行垃圾回收, 底部实现Runtime.getRuntime().gc();
    
            System.out.println("------After Gc------");
            //System.out.println("软引用对象是否还在="+employeeSoftReference.get());
            System.out.println("弱引用对象是否还在="+employeeWeakReference.get());
            System.out.println("虚引用对象是否还在="+employeePhantomReference.get());
    
            //向堆中填充数据模拟OOM
            List<Byte[]> list = new LinkedList<>();
            try{
                for (int i = 0; i < 10000; i++) {
                    //System.out.println("SoftReference == "+employeeSoftReference.get());
                    list.add(new Byte[1*1024*1024]);
                }
            }catch (Throwable e){
                System.out.println("------After OOM------");
                //System.out.println("软引用对象是否还在="+employeeSoftReference.get());
                System.out.println("弱引用对象是否还在="+employeeWeakReference.get());
                System.out.println("虚引用对象是否还在="+employeePhantomReference.get());
                e.printStackTrace();
            }
        }
    }
    

    输出结果:

    ------Before Gc------
    弱引用对象是否还在=com.chinapay.pointconsumer.test.ReferenceTest$Employee@57f23557
    虚引用对象是否还在=null
    ------After Gc------
    弱引用对象是否还在=null
    虚引用对象是否还在=null
    ------After OOM------
    弱引用对象是否还在=null
    虚引用对象是否还在=null
    java.lang.OutOfMemoryError: Java heap space
        at com.chinapay.pointconsumer.test.ReferenceTest.testReference(ReferenceTest.java:63)
        

    另外提示一下软引用和弱引用不能同时使用,否则gc后发现弱引用引用的对象还是存在的,因为软引用只有在oom后对象才会被当做垃圾回收。

    将上面的代码改成注释弱引用后,实验结果:

    ------Before Gc------
    软引用对象是否还在=com.chinapay.pointconsumer.test.ReferenceTest$Employee@57f23557
    虚引用对象是否还在=null
    ------After Gc------
    软引用对象是否还在=com.chinapay.pointconsumer.test.ReferenceTest$Employee@57f23557
    虚引用对象是否还在=null
    ------After OOM------
    软引用对象是否还在=null
    虚引用对象是否还在=null
    java.lang.OutOfMemoryError: Java heap space

    开发中,对于一些缓存的使用,如果对业务影响不大,在出现内存不足时希望优先被回收的,可以使用SoftReference或者WeakReference。

    参考文档:Java:Java的四种引用与其中区别_VoldemortQian的博客-CSDN博客_java四种引用的区别

    更多相关内容
  • 很早Java API就添加了弱引用(WeakReference)和软引用(SoftReference),但并不是所有的程序员都熟悉这两个概念
  • 强引用软引用弱引用、虚引用有什么区别?具体使用场景是什么? 典型回答 1.强引用 特点:我们平常典型编码Object obj = new Object()中的obj就是强引用。通过关键字new创建的对象所关联的引用

    在 Java 语言中,除了原始数据类型的变量,其他所有都是所谓的引用类型,指向各种不同的对象,理解引用对于掌握 Java 对象生命周期和 JVM 内部相关机制非常有帮助。

    在Java语言中,除了基本数据类型外,其他的都是指向各类对象的对象引用;Java中根据其生命周期的长短,将引用分为4类。

    强引用、软引用、弱引用、虚引用有什么区别?具体使用场景是什么?

    典型回答

    1.强引用

    特点:我们平常典型编码Object obj = new Object()中的obj就是强引用。通过关键字new创建的对象所关联的引用就是强引用。 当JVM内存空间不足,JVM宁愿抛出OutOfMemoryError运行时错误(OOM),使程序异常终止,也不会靠随意回收具有强引用的“存活”对象来解决内存不足的问题。对于一个普通的对象,如果没有其他的引用关系,只要超过了引用的作用域或者显式地将相应(强)引用赋值为 null,就是可以被垃圾收集的了,具体回收时机还是要看垃圾收集策略。

    应用场景:项目中到处都是。

    2.软引用

    特点:软引用通过SoftReference类实现。 软引用的生命周期比强引用短一些。只有当 JVM 认为内存不足时,才会去试图回收软引用指向的对象:即JVM 会确保在抛出 OutOfMemoryError 之前,清理软引用指向的对象。软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。后续,我们可以调用ReferenceQueue的poll()方法来检查是否有它所关心的对象被回收。如果队列为空,将返回一个null,否则该方法返回队列中前面的一个Reference对象。

    应用场景:软引用通常用来实现内存敏感的缓存。如果还有空闲内存,就可以暂时保留缓存,当内存不足时清理掉,这样就保证了使用缓存的同时,不会耗尽内存。

    具体实例:图片缓存框架中,“内存缓存”中的图片是以这种引用来保存,使得JVM在发生OOM之前,可以回收这部分缓存。

    3.弱引用

    特点:弱引用通过WeakReference类实现。 弱引用的生命周期比软引用短。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。由于垃圾回收器是一个优先级很低的线程,因此不一定会很快回收弱引用的对象。弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

    应用场景:弱应用同样可用于内存敏感的缓存。

    具体实例:在静态内部类中,经常会使用虚引用。例如,一个类发送网络请求,承担callback的静态内部类,则常以虚引用的方式来保存外部类(宿主类)的引用,当外部类需要被JVM回收时,不会因为网络请求没有及时回来,导致外部类不能被回收,引起内存泄漏。

    4.虚引用

    特点:虚引用也叫幻象引用,通过PhantomReference类来实现。无法通过虚引用访问对象的任何属性或函数。幻象引用仅仅是提供了一种确保对象被 finalize 以后,做某些事情的机制。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。

    ReferenceQueue queue = new ReferenceQueue ();
    PhantomReference pr = new PhantomReference (object, queue);
    

    程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取一些程序行动。

    应用场景:可用来跟踪对象被垃圾回收器回收的活动,当一个虚引用关联的对象被垃圾收集器回收之前会收到一条系统通知。

    具体实例:一种引用的get()方法返回总是null,所以,可以想象,在平常的项目开发肯定用的少。

    看到一个比较有意思的举例

    1. 强引用就像大老婆,关系很稳固。
    2. 软引用就像二老婆,随时有失宠的可能,但也有扶正的可能。
    3. 弱引用就像情人,关系不稳定,可能跟别人跑了。
    4. 幻像引用就是梦中情人,只在梦里出现过。

    知识扩展

    1.对象可达性状态流转分析

    在这里插入图片描述
    这是 Java 定义的不同可达性级别(reachability level),具体如下:

    1. 强可达(Strongly Reachable),就是当一个对象可以有一个或多个线程可以不通过各种引用访问到的情况。比如,我们新创建一个对象,那么创建它的线程对它就是强可达。
    2. 软可达(Softly Reachable),就是当我们只能通过软引用才能访问到对象的状态。
    3. 弱可达(Weakly Reachable),类似前面提到的,就是无法通过强引用或者软引用访问,只能通过弱引用访问时的状态。这是十分临近 finalize 状态的时机,当弱引用被清除的时候,就符合 finalize 的条件了。
    4. 幻象可达(Phantom Reachable),上面流程图已经很直观了,就是没有强、软、弱引用关联,并且 finalize 过了,只有幻象引用指向这个对象的时候。
    5. 当然,还有一个最后的状态,就是不可达(unreachable),意味着对象可以被清除了。

    所有引用类型,都是抽象类 java.lang.ref.Reference 的子类,你可能注意到它提供了 get() 方法:

    除了幻象引用(因为 get 永远返回 null),如果对象还没有被销毁,都可以通过 get 方法获取原有对象。这意味着,利用软引用和弱引用,可以将访问到的对象,重新指向强引用,也就是人为的改变了对象的可达性状态!这也是为什么上面图里有些地方画了双向箭头。

    所以,对于软引用、弱引用之类,垃圾收集器可能会存在二次确认的问题,以保证处于弱引用状态的对象,没有改变为强引用。

    但是,如果错误的保持了强引用(比如,赋值给了 static 变量),那么对象可能就没有机会变回类似弱引用的可达性状态了,就会产生内存泄漏。所以,检查弱引用指向对象是否被垃圾收集,也是诊断是否有特定内存泄漏的一个思路,如果我们的框架使用到弱引用又怀疑有内存泄漏,就可以从这个角度检查。

    2. 引用队列(ReferenceQueue)使用

    谈到各种引用的编程,就必然要提到引用队列。在创建各种引用并关联到响应对象时,可以选择是否需要关联引用队列,JVM 会在特定时机将引用 enqueue 到队列里,可以从队列里获取引用(remove 方法在这里实际是有获取的意思)进行相关后续逻辑。尤其是幻象引用,get 方法只返回 null,如果再不指定引用队列,基本就没有意义了。看看下面的示例代码。利用引用队列,可以在对象处于相应状态时(对于幻象引用,就是前面说的被 finalize 了,处于幻象可达状态),执行后期处理逻辑。

    Object counter = new Object();
    
    ReferenceQueue refQueue = new ReferenceQueue<>();
    
    PhantomReference<Object> p = new PhantomReference<>(counter, refQueue);
    
    counter = null;
    
    System.gc();
    
    try {
    
        // Remove 是一个阻塞方法,可以指定 timeout,或者选择一直阻塞
    
        Reference<Object> ref = refQueue.remove(1000L);
    
        if (ref != null) {
    
            // do something
    
        }
    
    } catch (InterruptedException e) {
    
        // Handle it
    
    }
    
    展开全文
  • 1 Java引用介绍 Java从1.2版本开始引入了4种引用,这4种引用的级别由高到低依次为: ... 软引用 > 弱引用 > 虚引用 ⑴强引用(StrongReference) 强引用是使用最普遍的引用。如果一个对象具...

    本文章转载自 https://www.cnblogs.com/skywang12345/p/3154474.html

    1 Java引用介绍

       Java从1.2版本开始引入了4种引用,这4种引用的级别由高到低依次为:

       强引用  >  软引用  >  弱引用  >  虚引用

    ⑴强引用(StrongReference)
        强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。

    ⑵软引用(SoftReference)

        如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。

        软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。

    ⑶弱引用(WeakReference)

        弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。

        弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

    ⑷虚引用(PhantomReference)

        “虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。

        虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。

     

    由于引用和内存回收关系紧密。下面,先通过实例对内存回收有个认识;然后,进一步通过引用实例加深对引用的了解。

     

     

     


    2 内存回收

    创建公共类MyDate,它的作用是覆盖finalize()函数:在finalize()中输出打印信息,方便追踪。

    说明:finalize()函数是在JVM回收内存时执行的,但JVM并不保证在回收内存时一定会调用finalize()。

    MyDate代码如下:

    复制代码

    package com.skywang.java;
    
    import java.util.Date;
    
    public class MyDate extends Date { 
    
        /** Creates a new instance of MyDate */
        public MyDate() {
        }
        // 覆盖finalize()方法
        protected void finalize() throws Throwable {
            super.finalize();
            System.out.println("obj [Date: " + this.getTime() + "] is gc");
        }   
    
        public String toString() {
            return "Date: " + this.getTime();
        }
    }

    复制代码

    在这个类中,对java.util.Date类进行了扩展,并重写了finalize()和toString()方法。

     

    创建公共类ReferenceTest,它的作用是定义一个方法drainMemory():消耗大量内存,以此来引发JVM回收内存。

    ReferenceTest代码如下:

    复制代码

    package com.skywang.java;
    
    public class ReferenceTest {   
        /** Creates a new instance of ReferenceTest */
        public ReferenceTest() {
        }   
        
        // 消耗大量内存
        public static void drainMemory() {
            String[] array = new String[1024 * 10];
            for(int i = 0; i < 1024 * 10; i++) {
                for(int j = 'a'; j <= 'z'; j++) {
                    array[i] += (char)j;
                }           
            }
        }
    } 

    复制代码

    在这个类中定义了一个静态方法drainMemory(),此方法旨在消耗大量的内存,促使JVM运行垃圾回收。

     

    有了上面两个公共类之后,我们即可测试JVM什么时候进行垃圾回收。下面分3种情况进行测试:

    情况1:清除对象

    实现代码

    复制代码

    package com.skywang.java;
    
    public class NoGarbageRetrieve {
    
        public static void main(String[] args) {
            MyDate date = new MyDate();
            date = null;
        }
    }

    复制代码

    运行结果

    <无任何输出>

    结果分析:date虽然设为null,但由于JVM没有执行垃圾回收操作,MyDate的finalize()方法没有被运行。

     

    情况2:显式调用垃圾回收

    实现代码: 

    复制代码

    package com.skywang.java;
    
    public class ExplicitGarbageRetrieve {
    
        /**
         * @param args
         */
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            MyDate date = new MyDate();
            date = null;
            System.gc();
        }
    
    }

    复制代码

    运行结果

    obj [Date: 1372137067328] is gc

    结果分析:调用了System.gc(),使JVM运行垃圾回收,MyDate的finalize()方法被运行。

     

    情况3:隐式调用垃圾回收

    实现代码: 

    复制代码

    package com.skywang.java;
    
    public class ImplicitGarbageRetrieve {
    
        /**
         * @param args
         */
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            MyDate date = new MyDate();
            date = null;
            ReferenceTest.drainMemory();
        }
    
    } 

    复制代码

    运行结果

    obj [Date: 1372137171965] is gc

    结果分析:虽然没有显式调用垃圾回收方法System.gc(),但是由于运行了耗费大量内存的方法,触发JVM进行垃圾回收。

     

    总结:JVM的垃圾回收机制,在内存充足的情况下,除非你显式调用System.gc(),否则它不会进行垃圾回收;在内存不足的情况下,垃圾回收将自动运行

     

     

     


    3、Java对引用的分类

    3.1 强引用

    实例代码

    复制代码

    package com.skywang.java;
    
    public class StrongReferenceTest {
    
        public static void main(String[] args) {
            MyDate date = new MyDate();
            System.gc();
        }
    }

    复制代码

    运行结果

    <无任何输出>

    结果说明:即使显式调用了垃圾回收,但是用于date是强引用,date没有被回收。

     

    3.2 软引用

    实例代码

    复制代码

    package com.skywang.java;
    
    import java.lang.ref.SoftReference;
    
    public class SoftReferenceTest {
    
        public static void main(String[] args) {
            SoftReference ref = new SoftReference(new MyDate());
            ReferenceTest.drainMemory();
        }
    }

    复制代码

    运行结果

    <无任何输出>

    结果说明:在内存不足时,软引用被终止。软引用被禁止时,

    SoftReference ref = new SoftReference(new MyDate());
    ReferenceTest.drainMemory();

    等价于

    MyDate date = new MyDate();

    // 由JVM决定运行
    If(JVM.内存不足()) {
    date = null;
    System.gc();
    }

     

    3.3 弱引用

    示例代码: 

    复制代码

    package com.skywang.java;
    
    import java.lang.ref.WeakReference;
    
    public class WeakReferenceTest {
    
        public static void main(String[] args) {
            WeakReference ref = new WeakReference(new MyDate());
            System.gc(); 
        }
    }

    复制代码

    运行结果

    obj [Date: 1372142034360] is gc

    结果说明:在JVM垃圾回收运行时,弱引用被终止.

    WeakReference ref = new WeakReference(new MyDate());
    System.gc();

    等同于:

    MyDate date = new MyDate();

    // 垃圾回收
    If(JVM.内存不足()) {
    date = null;
    System.gc();
    }

     

    3. 4 假象引用

    示例代码: 

    复制代码

    package com.skywang.java;
    
    import java.lang.ref.ReferenceQueue;
    import java.lang.ref.PhantomReference;
    
    public class PhantomReferenceTest {
    
        public static void main(String[] args) {
            ReferenceQueue queue = new ReferenceQueue();
            PhantomReference ref = new PhantomReference(new MyDate(), queue);
            System.gc();
        }
    }

    复制代码

    运行结果

    obj [Date: 1372142282558] is gc

    结果说明:假象引用,在实例化后,就被终止了。

    ReferenceQueue queue = new ReferenceQueue();
    PhantomReference ref = new PhantomReference(new MyDate(), queue);
    System.gc();

    等同于:

    MyDate date = new MyDate();
    date = null;

     

     

    可以用以下表格总结上面的内容: 

    级别

    什么时候被垃圾回收

    用途

    生存时间

    强引用

    从来不会

    对象的一般状态

    JVM停止运行时终止

    软引用

    在内存不足时

    对象简单?缓存

    内存不足时终止

    弱引用

    在垃圾回收时

    对象缓存

    gc运行后终止

    虚引用

    Unknown

    Unknown

    Unknown

     

     

     

    展开全文
  • Java:强引用软引用弱引用和虚引用

    万次阅读 多人点赞 2019-01-02 16:56:19
    一、强引用 二、软引用 三、弱引用 四、虚引用 五、总结 在JDK1.2以前的版本中,当一个对象不被任何变量引用,那么程序就无法再使用这个对象。也就是说,只有对象处于可触及状态,程序才能使用它。这就像在商店...

    目录

    一、强引用

    二、软引用

    三、弱引用

    四、虚引用

    五、总结


    在JDK1.2以前的版本中,当一个对象不被任何变量引用,那么程序就无法再使用这个对象。也就是说,只有对象处于可触及状态,程序才能使用它。这就像在商店购买了某样物品后,如果有用就一直保留它,否则就把它扔到垃圾箱,由清洁工人收走。一般说来,如果物品已经被扔到垃圾箱,想再把它捡回来使用就不可能了。

    但有时候情况并不这么简单,可能会遇到可有可无的"鸡肋"物品。这种物品现在已经无用了,保留它会占空间,但是立刻扔掉它也不划算,因为也许将来还会派用场。对于这样的可有可无的物品:如果家里空间足够,就先把它保留在家里,如果家里空间不够,即使把家里所有的垃圾清除,还是无法容纳那些必不可少的生活用品,那么再扔掉这些可有可无的物品。

    在Java中,虽然不需要程序员手动去管理对象的生命周期,但是如果希望某些对象具备一定的生命周期的话(比如内存不足时JVM就会自动回收某些对象从而避免OutOfMemory的错误)就需要用到软引用和弱引用了。

    从Java SE2开始,就提供了四种类型的引用:强引用、软引用、弱引用和虚引用。Java中提供这四种引用类型主要有两个目的:第一是可以让程序员通过代码的方式决定某些对象的生命周期;第二是有利于JVM进行垃圾回收。

    一、强引用

    之前我们使用的大部分引用实际上都是强引用,这是使用最普遍的引用。比如下面这段代码中的object和str都是强引用:

    Object object = new Object();
    String str = "StrongReference";

    如果一个对象具有强引用,那就类似于必不可少的物品,不会被垃圾回收器回收。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不回收这种对象。

    public class StrongReference {
    	public static void main(String[] args) {
    		new StrongReference().method1();
    	}
    	public void method1(){
    		Object object=new Object();
    		Object[] objArr=new Object[Integer.MAX_VALUE];
    	}
    }

    运行结果:

    当运行至Object[] objArr = new Object[Integer.MAX_VALUE]时,如果内存不足,JVM会抛出OOM错误也不会回收object指向的对象。不过要注意的是,当method1运行完之后,object和objArr都已经不存在了,所以它们指向的对象都会被JVM回收。

    如果想中断强引用和某个对象之间的关联,可以显示地将引用赋值为null,这样一来的话,JVM在合适的时间就会回收该对象。

    比如ArraryList类的clear方法中就是通过将引用赋值为null来实现清理工作的

    public void clear() {
          modCount++;
    
          // Let gc do its work
          for (int i = 0; i < size; i++)
              elementData[i] = null;
    
          size = 0;
    }

    在ArrayList类中定义了一个私有的变量elementData数组,在调用方法清空数组时可以看到为每个数组内容赋值为null。不同于elementData=null,强引用仍然存在,避免在后续调用 add()等方法添加元素时进行重新的内存分配。使用如clear()方法中释放内存的方法对数组中存放的引用类型特别适用,这样就可以及时释放内存。

     

    二、软引用

    软引用是用来描述一些有用但并不是必需的对象,在Java中用java.lang.ref.SoftReference类来表示。对于软引用关联着的对象,只有在内存不足的时候JVM才会回收该对象。因此,这一点可以很好地用来解决OOM的问题,并且这个特性很适合用来实现缓存:比如网页缓存、图片缓存等。

    软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被JVM回收,这个软引用就会被加入到与之关联的引用队列中。

    import java.lang.ref.SoftReference;
    
    public class SoftRef {  
    
        public static void main(String[] args){  
            System.out.println("start");            
            Obj obj = new Obj();            
            SoftReference<Obj> sr = new SoftReference<Obj>(obj);  
            obj = null;  
            System.out.println(sr.get());  
            System.out.println("end");     
        }       
    }  
    
    class Obj{  
        int[] obj ;  
        public Obj(){  
            obj = new int[1000];  
        }  
    }

    当内存足够大时可以把数组存入软引用,取数据时就可从内存里取数据,提高运行效率

    软引用在实际中有重要的应用,例如浏览器的后退按钮,这个后退时显示的网页内容可以重新进行请求或者从缓存中取出:

    (1)如果一个网页在浏览结束时就进行内容的回收,则按后退查看前面浏览过的页面时,需要重新构建

    (2)如果将浏览过的网页存储到内存中会造成内存的大量浪费,甚至会造成内存溢出这时候就可以使用软引用

     

    三、弱引用

    弱引用也是用来描述非必需对象的,当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。在java中,用java.lang.ref.WeakReference类来表示。

    弱引用与软引用的区别在于只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程, 因此不一定会很快发现那些只具有弱引用的对象。所以被软引用关联的对象只有在内存不足时才会被回收,而被弱引用关联的对象在JVM进行垃圾回收时总会被回收。

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

    运行结果:

    在使用软引用和弱引用的时候,我们可以显示地通过System.gc()来通知JVM进行垃圾回收,但是要注意的是,虽然发出了通知,JVM不一定会立刻执行,也就是说这句是无法确保此时JVM一定会进行垃圾回收的。

     

    弱引用还可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

    Object o = new Object(); //只要o还指向对象就不会被回收
    WeakReference<Object> wr = new WeakReference<Object>(o);

    当要获得weak reference引用的object时, 首先需要判断它是否已经被回收,如果wr.get()方法为空, 那么说明weakCar指向的对象已经被回收了。

    应用场景:如果一个对象是偶尔的使用,并且希望在使用时随时就能获取到,但又不想影响此对象的垃圾收集,那么应该用 Weak Reference 来记住此对象。或者想引用一个对象,但是这个对象有自己的生命周期,你不想介入这个对象的生命周期,这时候就应该用弱引用,这个引用不会在对象的垃圾回收判断中产生任何附加的影响。

     

    四、虚引用

    虚引用和前面的软引用、弱引用不同,它并不影响对象的生命周期。在java中用java.lang.ref.PhantomReference类表示。如果一个对象与虚引用关联,则跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收。虚引用主要用来跟踪对象被垃圾回收的活动。

    虚引用必须和引用队列关联使用,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会把这个虚引用加入到与之 关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

    import java.lang.ref.PhantomReference;
    import java.lang.ref.ReferenceQueue;
     
     
    public class PhantomRef {
        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());
        }
    }

     

    五、总结

    引用类型被回收时间用途生存时间
    强引用从来不会对象的一般状态JVM停止运行时
    软引用内存不足时对象缓存内存不足时
    弱引用jvm垃圾回收时对象缓存gc运行后
    虚引用未知未知未知

    在实际程序设计中一般很少使用弱引用与虚引用,使用软引用的情况较多,这是因为软引用可以加速JVM对垃圾内存的回收速度,可以维护系统的运行安全,防止内存溢出(OutOfMemory)等问题的产生

     

    利用软引用和弱引用解决OOM问题:假如有一个应用需要读取大量的本地图片,如果每次读取图片都从硬盘读取,则会严重影响性能,但是如果全部加载到内存当中,又有可能造成内存溢出,此时使用软引用可以解决这个问题。

    设计思路是:用一个HashMap来保存图片的路径和相应图片对象关联的软引用之间的映射关系,在内存不足时,JVM会自动回收这些缓存图片对象所占用的空间,从而有效地避免了OOM的问题。


    参考资料:

    1、https://www.cnblogs.com/dolphin0520/p/3784171.html

    2、https://blog.csdn.net/junjunba2689/article/details/80601729

    展开全文
  • Java基础篇 - 强引用弱引用软引用和虚引用

    万次阅读 多人点赞 2018-09-09 08:58:21
    引用计数:Java堆中每一个对象都有一个引用计数属性,引用每新增1次计数加1,引用每释放1次计数减1。 在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无法再使用这个对象。也就是说,只有对象...
  • 今天我们浅谈一下java的四种引用,分别是强引用软引用弱引用与序引用。在谈引用之前我们不得不提一下Java的垃圾回收器机制GC,GC算是Java的一大特点,我们都知道c语言是不能自动释放内存的需要程序员去操作的,...
  • referenceInJava:Java强引用软引用弱引用以及虚引用测试项目
  • 在Java中,除了基础数据类型的变量,其他所有都是所谓的引用类型,...Java将对象的引用划分为4个等级,分别是:强引用软引用弱引用和虚引用。根据不同引用类型可以灵活控制对象的生命周期,提高对象的回收效率。...
  • 主要介绍了详解Java对象的弱和引用+ReferenceQueue的相关资料,需要的朋友可以参考下
  • 弱引用软引用区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它 所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。 如果这个...
  • 所谓强引用( “Strong” Reference),就是我们最常见的普通对象引用,只要还有强引用指向一个对象,就能表明对象还“活着”,垃圾收集器不会碰这种对象。对于一个普通的对象,如果没有其他的引用关系,只要超过了...
  • 强引用 弱引用 软引用 虚引用
  • Java从JDK1.2版本开始,就把对象的引用分为四种级别,从而使程序能更加灵活的...这四种级别由高到低依次为:强引用软引用弱引用和虚引用。本文给大家介绍Android利用软引用和弱引用避免OOM,需要的朋友一起学习吧
  • 文章目录 背景 1、强引用(StrongReference) 回收内存原理举例 2、软引用(SoftReference) 3、... 参考: Java 关于强引用软引用弱引用和虚引用的区别与用法 Java 如何有效地避免OOM:善于利用软引用和弱引用
  • java根据其生命周期的长短又将引用类型分为强引用软引用弱引用、虚引用; 强引用:new一个对象就是强引用,例如 Object obj = new Object();当JVM的内存空间不足时,宁愿抛出OutOfMemoryError使得程序异常终止...
  • 这四种级别由高到低依次为:强引用软引用弱引用和虚引用。强引用是最常用的,软引用应用的情况比较多,弱引用用的比较少,虚引用基本不用。 1.强引用 常用的引用都是强引用,如果一个对象具有强引用,GC绝...
  • 强引用弱引用软引用和虚引用

    千次阅读 2020-07-31 15:34:06
    前言 Java执行GC判断对象是否存活有两种方式其中一种是引用计数。 引用计数:Java堆中每一个对象都有一个引用计数属性,引用每新增1次计数加1...这4种级别由高到低依次为:强引用软引用弱引用和虚引用。 1. 强
  • 强引用软引用弱引用、幻象引用通俗理解通俗理解状态转换关系各状态详解强引用(StrongReference)软引用(SoftReference)弱引用虚引用总结:鸣谢 之前忙于工作,都没有好好地去研究基础的知识,周末挤出点时间去...
  • Java中的引用类型分为四种:强引用软引用弱引用和虚引用 强引用 例如:Object o = new Object()就是强引用关系,这是一种最为常见的引用关系,而被强引用关联的对象,只要引用关系还存在,则不会被垃圾收集器...
  • 整体架构如图: 强引用(默认支持模式): ...强引用就是我们最常见的普通对象引用,只要还有强引用指向一个对象,就能表明对象还“活着”,垃圾回收器不会碰这种对象。例如: public class StrongRefDemo { publ...
  • 强引用软引用弱引用、幻象引用有什么区别? 在Java语言中,除了原始数据类型的变量,其他所有都是所谓的引用类型,指向各种不同的对象,理解引用对于掌握Java对象生命周期JVM内部相关机制非常有帮助。 今天我...
  • 软引用(Soft Reference)、 弱引用(Weak Reference) 虚引用(Phantom Reference) 1.强引用 强引用是最传统的“引用”的定义,是指在程序代码之中普遍存在的引用赋值,即类似“Object obj=new O.
  • 1、强引用(StrongReference) 最普遍的一种引用方式,如String s = “abc”,变量s就是字符串“abc”的强引用,只要强引用...一般用于实现内存敏感的高速缓存,软引用可以引用队列ReferenceQueue联合使用,如果...
  • Java四大引用详解:强引用软引用弱引用、虚引用
  • 主要介绍了Java中强引用,软引用弱引用概念解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 对象引用类型分为强引用软引用弱引用和虚引用。 强引用:就是我们一般声明对象是时虚拟机生成的引用,强引用环境下,垃圾回收时需要严格判断当前对象是否被强引用,如果被强引用,则不会被垃圾回收 软引用:...
  • 先说一下四种引用的定义,可以结合代码讲一下,也可以扩展谈到ThreadLocalMap里弱引用用处。 强引用是最传统的“引用”的定义,是指在程序代码之中普遍存在的引用赋值,即类似“Objectobj=new Object()”这种引用...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 39,540
精华内容 15,816
热门标签
关键字:

强引用,软引用和弱引用的区别