首页 > 其他分享 >jvm之强软弱虚引用

jvm之强软弱虚引用

时间:2023-04-23 10:37:30浏览次数:27  
标签:get 对象 System 回收 引用 jvm 之强 软弱 User


强软弱虚引用

在java中,除了基本数据类型的变量外,其他所有的变量都是引用类型,指向堆上各种不同的对象。

在jvm中,除了我们常用的强引用外,还有软引用、弱引用、虚引用,这四种引用类型的生命周期与jvm的垃圾回收过程息息相关。

那么这四种引用类型有什么区别?具体使用场景是什么?

所有引用类型,都是抽象类java.lang.ref.Reference的子类,这个类的主要方法为get()方法:

public T get() {
        return this.referent;
    }

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

强引用

强引用(Strong references)就是直接new一个普通对象,表示一种比较强的引用关系,只要还有强引用对象指向一个对象,那么表示这个对象还活着,垃圾收集器宁可抛出OOM异常,也不会回收这个对象。

例如下面的引用u就是一个强引用。

public class StrongReferenceDemo {
    public static void main(String[] args) throws IOException {
        User u = new User();
        System.out.println(u);
        u = null;
        System.gc();

        System.in.read();
    }
}

public class User {

    @Override
    protected void finalize() throws Throwable {
        System.out.println("call User finalize() method");
    }
}

上面的User对象重写了父类Object的finalize(),在GC准备释放对象所占用的内存空间之前,它将首先调用finalize()方法。

在Java中,由于GC的自动回收机制,因而并不能保证finalize方法会被及时地执行(垃圾对象的回收时机具有不确定性),也不能保证它们会被执行(程序由始至终都未触发垃圾回收),所以finalize不推荐使用,这里只是为了演示垃圾回收的过程。

另外finalize()最多只会被调用一次,也就是只能利用finalize()为对象续命一次。

软引用

软引用用于存储一些可有可无的东西,例如缓存,当系统内存充足时,这些对象不会被回收,当系统内存不足时也是GC时才会回收这些对象,如果回收完这些对象后内存还是不足,就会抛出OOM异常。

// vm args: -Xmx36m -XX:+PrintGCDetails
public class SoftReferenceDemo {
    public static void main(String[] args) throws InterruptedException {

        SoftReference<User> softReference = new SoftReference<>(new User()); // 软引用

        System.out.println(softReference.get());

        System.gc(); 

        TimeUnit.SECONDS.sleep(3); // wait gc thread run

        System.out.println(softReference.get()); // User对象不会被回收

        byte[] bytes = new byte[1024 * 1024 * 10]; // 分配一个大对象使得堆空间不足,软引用对象会在OOM之前先被回收
        System.out.println(softReference.get());

    }
}

在上面的例子中,第一次发生gc时,User对象不会被回收,第二次发生gc时由于堆空间不足,会先回收软引用的对象,回收完了还是空间不足,最后抛出OOM异常。

弱引用

弱引用(WeakReference)并不能使对象豁免垃圾回收,仅仅是提供一种访问在弱引用状态下对象的途径。只要发生gc,弱引用对象就会被回收。ThreadLocal中就使用了WeakReference来避免内存泄漏。

public class WeakReferenceDemo {

    public static void main(String[] args) throws InterruptedException {

        WeakReference<User> weakReference = new WeakReference<>(new User());

        System.out.println(weakReference.get());

        System.gc();

        TimeUnit.SECONDS.sleep(3); // wait gc thread run

        System.out.println(weakReference.get()); // null
    }
}

上面的例子只要发生gc,User对象就会被垃圾收集器回收。

虚引用

虚引用必须和引用队列(ReferenceQueue)联合使用。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。

虚引用主要用来跟踪对象被垃圾回收的活动。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象之前,把这个虚引用加入到与之关联的引用队列中。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

// vm args: -Xms4m -XX:+PrintGC
public class PhantomReferenceDemo {

    public static void main(String[] args) throws IOException, InterruptedException {
        ReferenceQueue<User> referenceQueue = new ReferenceQueue<>(); // 引用队列
        PhantomReference<User> phantomReference = new PhantomReference<>(new User(), referenceQueue); // 虚引用

        System.out.println(phantomReference.get()); // null


        new Thread(() -> {
            while (true) {
                Reference<? extends User> poll = referenceQueue.poll();
                if (poll != null) {
                    System.out.println("--- 虚引用对象被jvm回收了 ---- " + poll);
                    System.out.println("--- 回收对象 ---- " + poll.get()); // null
                }
            }
        }).start();

        TimeUnit.SECONDS.sleep(1);

        System.gc();

        System.in.read();

    }

    private static class User {

        private int[] bytes = new int[1024 * 1024 * 5];

        @Override
        protected void finalize() throws Throwable {
            System.out.println("call User finalize() method");
        }
    }
}

实际上,虚引用的get()方法总是返回null。

基于虚引用,有一个更加优雅的实现方式,那就是Cleaner,可以用来替代Object类的finalizer方法,在DirectByteBuffer中用来回收堆外内存。

更多精彩内容关注本人公众号:架构师升级之路

jvm之强软弱虚引用_虚引用


标签:get,对象,System,回收,引用,jvm,之强,软弱,User
From: https://blog.51cto.com/u_6784072/6216455

相关文章

  • jvm之垃圾收集器
    垃圾收集器先看下图中HotSpot虚拟机所包含的收集器:图中展示了9种作用于不同分代的收集器,如果两个收集器之间存在连线,则说明它们可以搭配使用。虚拟机所处的区域则表示它是属于新生代还是老年代收集器。新生代收集器:Serial、ParNew、ParallelScavenge老年代收集器:CMS、SerialOld、......
  • jvm之垃圾回收算法
    垃圾回收算法哪些内存需要回收jvm的内存模型中将内存划分为程序计数器、虚拟机栈、本地方法栈、堆、方法区。其中程序计数器、虚拟机栈、本地方法栈属于线程私有的内存空间,与线程的生命周期保持一致,不需要手动回收内存。方法区中存放的是类的结构信息,对方法区的回收其实就是对类进......
  • jvm之线程上下文加载器与SPI
    线程上下文加载器线程上下文类加载器(ThreadContextClassLoader,简称TCCL)是从JDK1.2开始引入的。类java.lang.Thread中的方法getContextClassLoader()和setContextClassLoader(ClassLoadercl)用来获取和设置线程的上下文类加载器。如果没有通过setContextClassLoader(ClassLoader......
  • jvm如何打破双亲委托机制
    打破双亲委托机制重写父类ClassLoader的loadClass方法packagecom.morris.jvm.classloader;publicclassBreakDelegateClassLoaderextendsMyClassLoader{@OverrideprotectedClass<?>loadClass(Stringname,booleanresolve)throwsClassNotFoundException{......
  • JVM内存模型
    JVM内存模型JVM的内存模型也就是JVM中的内存布局,不要与java的内存模型(与多线程相关)混淆。下图是jdk8jvm内存模型图:程序计数器程序计数器是当前线程所执行的字节码的行号指示器。JVM支持多个线程同时运行,每个线程都会根据CPU时间片来回切换,那么如果当前线程获得时间片了,怎么知道它......
  • Java虚拟机之JVM工具监控调优
    我是攻城师(woshigcs)前几篇我们学习了,JVM里面的运行结构,GC算法,以及各种垃圾收集器的优劣点,那么本篇我们来看下如何使用一些虚拟机性能监控工具,来监控和快速处理故障,当JVM出现一些故障时,我们通常从如下的几个方面进行着手分析,包括运行日志,异常堆栈,GC日志,线程快照(threaddump/javacor......
  • JDK,JRE,JVM之间的关系
    JDK,JRE,JVM三者之间的关系JDK=JRE+开发工具集(例如javac编译工具等)JRE=JVM+JavaSE标准类库JDK(Java开发工具包)JRE(Java运行环境)JVM(Java虚拟机)......
  • 一文回顾JVM
     ......
  • 从原理聊JVM(一):染色标记和垃圾回收算法
    作者:京东科技 康志兴1JVM运行时内存划分1.1运行时数据区域•方法区属于共享内存区域,存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。运行时常量池,属于方法区的一部分,用于存放编译期生成的各种字面量和符号引用。JDK1.8之前,Hotspot虚拟机对方法区......
  • JVM垃圾回收机制之对象回收算法
    在前面的文章中,介绍了JVM内存模型分为:堆区、虚拟机栈、方法区、本地方法区和程序计数器,其中堆区是JVM中最大的一块内存区域,在Java中的所有对象实例都保存在此区域,它能被所有线程共享。在Java中还有一个重要的机制:GC(垃圾收集器),堆是GC管理的主要区域,本文会带大家了解GC机制。GC......