Java中的引用类型和使用场景详细

目录
  • 1、强引用
  • 2、软引用
  • 3、弱引用
  • 4、弱引用的使用场景
  • 5、虚引用

Java中的引用类型有哪几种?

Java中的引用类型分成 强引用 , 软引用 , 弱引用 , 虚引用 。

1、强引用

没有引用指向这个对象,垃圾回收会回收

package git.snippets.juc;

import java.io.IOException;

public class NormalRef {
    public static void main(String[] args) throws IOException {
        M m = new M();
        m = null;
        System.gc();
        System.in.read();
    }
    static class M {
        M() {}
        @Override
        protected void finalize() throws Throwable {
            System.out.println("finalized");
        }
    }
}

2、软引用

当有一个对象被一个软引用所指向的时候,只有系统内存不够用的时候,才会被回收,可以用做缓存(比如缓存大图片)

示例如下代码:注:执行以下方法的时候,需要把VM options设置为 -Xms20M -Xmx20M

package git.snippets.juc;

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

/**
 * heap将装不下,这时候系统会垃圾回收,先回收一次,如果不够,会把软引用干掉
 * 软引用,适合做缓存
 * 示例需要把Vm options设置为:-Xms20M -Xmx20M
 */
public class SoftRef {
    public static void main(String[] args) throws IOException {
        SoftReference<byte[]> reference = new SoftReference<>(new byte[1024 * 1024 * 10]);
        System.out.println(reference.get());
        System.gc();
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(reference.get());
        byte[] bytes = new byte[1024 * 1024 * 10];
        System.out.println(reference.get());
        System.in.read();
    }
}

上述代码在第一次执行 System.out.println(reference.get()) 时候,由于堆的最大最小值都是 20M ,而我们分配的 byte 数组是 10M ,没有超过最大堆内存,所以执行垃圾回收,软引用不被回收,后续又调用了 byte[] bytes = new byte[1024 * 1024 * 10]; 再次分配了 10M 内存,此时堆内存已经超过设置的最大值,会进行回收,所以最后一步的 System.out.println(reference.get()); 无法 get 到数据。

3、弱引用

只要垃圾回收,就会回收。如果有一个强引用指向弱引用中的这个对象,如果这个强引用消失,这个对象就应该被回收。一般用在容器里面。

代码示例如下:

package git.snippets.juc;

import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;

/**
 * 弱引用遭到gc就会回收
 * ThreadLocal应用,缓存应用,WeakHashMap
 */
public class WeakRef {
    public static void main(String[] args) {
        WeakReference<T> reference = new WeakReference<>(new T());
        System.out.println(reference.get());
        System.gc();
        System.out.println(reference.get());
    }
    static class T {
        T() {}
        @Override
        protected void finalize() {
            System.out.println("finalized");
        }
    }
}

如果执行了一次 GC reference.get() 获取到的值即为空。

4、弱引用的使用场景

弱引用的一个典型应用场景就是 ThreadLocal ,以下是 ThreadLocal 的的简要介绍

set方法:

public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

get方法:

public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

ThreadLocalMap 是当前线程的一个成员变量,所以,其他线程无法读取当前线程设置的 ThreadLocal 值。

ThreadLocal.ThreadLocalMap threadLocals = null;

ThreadLocal 的主要应用场景

场景一:每个线程需要一个独享的对象:假设有100个线程都需要用到 SimpleDateFormat 类来处理日期格式,如果共用一个 SimpleDateFormat ,就会出现线程安全问题,导致数据出错,如果加锁,就会降低性能,此时使用 ThreadLocal ,给每个线程保存一份自己的本地 SimpleDateFormat ,就可以同时保证线程安全和性能需求。

场景二:每个线程内部保存全局变量,避免传参麻烦:假设一个线程的作用是拿到前端用户信息,逐层执行 Service1 Service2 Service3 Service4 层的业务逻辑,其中每个业务层都会用到用户信息,此时一个解决办法就是将 User 信息对象作为参数层层传递,但是这样会导致代码冗余且不利于维护。此时可以将 User 信息对象放入当前线程的 Threadlocal 中,就变成了全局变量,在每一层业务层中,需要使用的时候直接从 Threadlocal 中获取即可。

场景三: Spring 的声明式事务,数据库连接写在配置文件,多个方法可以支持一个完整的事务,保证多个方法是用的同一个数据库连接(其实就是放在 ThreadLocal 里面)

了解了 ThreadLocal 简要介绍以后,我们可以深入理解一下 ThreadLocal 的一个内部原理,前面提到, ThreadLocal 的 set 方法实际上是往当前线程的一个 threadLocals 表中插入一条记录,而这个表中的记录都存在一个 Entry 对象中,这个对象有一个key和一个value, key 就是当前线程的 ThreadLocal 对象。

static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

这个 Entry 对象继承了 WeakReference , 且构造函数调用了 super(k) , 所以 Entry 中的 key 是通过一个弱引用指向的 ThreadLocal ,所以,我们在主方法中调用

ThreadLocal<Object> tl = new ThreadLocal<>();

tl 是通过强引用指向这个 ThreadLocal 对象。

当前线程的 threadLocalMap 中的 key 是通过弱引用指向 ThreadLocal 对象,这样就可以保证,在 tl 指向空以后,这个 ThreadLocal 会被回收,否则,如果 threadLocalMap 中的 key 是强引用指向 ThreadLocal 对象话,这个 ThreadLocal 对象永远不会被回收。就会导致内存泄漏。

但是,即便 key 用弱引用指向 ThreadLocal 对象, key 值被回收后, Entry 中的 value 值就无法被访问到了,且 value 是通过强引用关联,所以,也会导致内存泄漏,所以,每次在 ThreadLocal 中的对象不用了,记得要调用 remove 方法,把对应的 value 也给清掉。

5、虚引用

用于管理堆外内存回收

虚引用关联了一个对象,以及一个队列,只要垃圾回收,虚引用就被回收,一旦虚引用被回收,虚引用会被装到这个队列,并会收到一个通知(如果有值入队列,会得到一个通知)所以,如果想知道虚引用何时被回收,就只需要不断监控这个队列是否有元素加入进来了。

虚引用里面关联的对象用get方法是无法获取的。

import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.LinkedList;
import java.util.List;

// 配置 -Xms20M -Xmx20M
public class PhantomRef {
    private static final List<Object> LIST = new LinkedList<>();
    private static final ReferenceQueue<P> QUEUE = new ReferenceQueue<>();

    public static void main(String[] args) {
        PhantomReference<P> phantomReference = new PhantomReference<>(new P(), QUEUE);
        new Thread(() -> {
            while (true) {
                LIST.add(new byte[1024 * 1024]);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    Thread.currentThread().interrupt();
                }
                System.out.println(phantomReference.get());
            }
        }).start();

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

        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    static class P {
        @Override
        protected void finalize() throws Throwable {
            System.out.println("finalized");
        }
    }
}

6、虚引用的应用场景

JDK的 NIO 包中有一个 DirectByteBuffer , 这个 buffer 指向的是堆外内存,所以当这个 buffer 设置为空的时候,Java的垃圾回收无法回收,所以,可以用虚引用来管理这个 buffer ,当我们检测到这个虚引用被垃圾回收器回收的时候,可以做出相应的处理,去回收堆外内存。

到此这篇关于Java中的引用类型和使用场景详细的文章就介绍到这了,更多相关Java中的引用类型和使用场景内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解Java的引用类型及使用场景

    每种编程语言都有自己操作内存中元素的方式,例如在 C 和 C++ 里是通过指针,而在 Java 中则是通过"引用".在 JDK.1.2 之后,Java 对引用的概念进行了扩充,将引用分为了:强引用(Strong Reference).软引用(Soft Reference).弱引用(Weak Reference).虚引用(Phantom Reference)4 种,这 4 种引用的强度依次减弱,今天这篇文章就简单介绍一下这四种类型,并简单说一下他们的使用场景. 1. 强引用(Strong

  • java的引用类型的详细介绍

    •强引用(FinalReference),在java中,有点像C++的指针,通过引用,可以对堆中的对象进行操作.强引用具备以下特点: 1.强引用可以直接访问目标对象:2.强引用所指向的对象在任务时候都不会被系统回收:3.强引用可能导致内存泄露.•软引用(SoftReference),软引用对象,在响应内存需要时,由垃圾回收器决定是否清除此对象.一个持有软件引用的对象,不会被JVM很快回收,只要有足够的内存,软件引用便可能在内存中存活相当长的时间,软引用对象最常用于实现内存敏感的缓存:•弱引用(W

  • 四种引用类型在JAVA Springboot中的使用详解

    目录 概念介绍 01.  强引用 02.  软引用 03.  弱引用 04.  虚引用 对象可达性 Springboot源码中的使用 总结 概念介绍 不同的引用类型,主要体现的是对象不同的可达性(reachable)状态和对垃圾收集的影响. 01.  强引用 这个就是我们创建的普通对象了~ 当该对象被显示地赋值为 null 时,或者没有被其他存活的对象继续引用时,它就会成为垃圾收集器的目标,等待被收回 02.  软引用 软引用( SoftReference ) , 当内存不足 时会被回收 比如

  • 简述Java中的四种引用类型

    简介 从JDK1.2版本开始,把对象的引用分为四种级别,从而使程序能更加灵活的控制对象的生命周期.这四种级别由高到低依次为:强引用.软引用.弱引用和虚引用,下面分别介绍下这四种引用. 强引用 强引用是最常用的引用类型,如下所示,new Object()会创建一个Object对象并存储在堆上,变量object存储对该对象的强引用. Object object = new Object(); 强引用不会被垃圾回收,所以要想回收该对象,则应该将指向该对象的变量显示设为null,这样该对象就由强引用转变

  • Java中的引用类型和使用场景详细

    目录 1.强引用 2.软引用 3.弱引用 4.弱引用的使用场景 5.虚引用 Java中的引用类型有哪几种? Java中的引用类型分成 强引用 , 软引用 , 弱引用 , 虚引用 . 1.强引用 没有引用指向这个对象,垃圾回收会回收 package git.snippets.juc; import java.io.IOException; public class NormalRef { public static void main(String[] args) throws IOExcepti

  • java中Object类4种方法详细介绍

    目录 Object(四大方法): hashCode()方法: equals()方法: getClass()方法: toString()方法: 总结 Object(四大方法): 文章干货满满,耐性看完~~何为Object?首先先来看看官方对Object的介绍:在这里附上Java官方的查阅工具:https://docs.oracle.com/en/java/javase/17/docs/api/index.html 由官方介绍可见,object属于Java.lang包内的一个类,而且提供了很多种方法

  • 浅谈Java 中的引用类型

    Java 中的引用类型:强引用.软引用.弱引用和虚引用 强引用 如 Object object = new Object(),那 object 就是一个强引用,如果一个对象具有强引用,垃圾回收器就永远不会回收它. 软引用 软引用用来描述一些还有用但非必需的对象.在内存即将发生内存溢出之前,会把这些对象列进回收范围之中进行二次垃圾回收.如果这次回收还没有足够内存,才会发生内存溢出现象. 另:软引用可用来实现内存敏感的高速缓存. 弱引用 用来描述非必需的对象.被弱引用关联的对象只能存活到下一次垃圾收

  • java中的引用类型之强软弱虚详解

    前言 java中的引用类型共4种:强软弱虚,具体每种类型的特点和应用场景.记录下.本文是看了马士兵老师的视频后记录整理的.加深印象. 基本概念 1. 强引用 强引用是使用最普遍的引用.如果一个对象具有强引用,那垃圾回收器绝不会回收它.当内存空间不足时,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题. 显式地设置M对象为null,或让其超出对象的生命周期范围,则gc认为该对象不存在引用,这时就可以回收这个对象 示例代

  • Java中线程上下文类加载器超详细讲解使用

    目录 一.什么是线程上下文类加载器 1.1.重要性 1.2.使用场景 二.ServiceLoader简单介绍 三.案例 3.1.使用ServiceLoader加载mysql驱动 3.2.Class.forName加载Mysql驱动 3.2.1.com.mysql.jdbc.Driver 3.2.2.java.sql.DriverManager初始化 3.2.3.调用DriverManager的registerDriver方法 3.2.4.执行DriverManager.getConnection

  • 浅谈java中unmodifiableList方法的应用场景

    java对象中primitive类型变量可以通过不提供set方法保证不被修改,但对象的List成员在提供get方法后,就可以随意add.remove改变其结构,这不是希望的结果.网上看了下,发现Collections的静态方法unmodifiableList可以达到目的.方法原型为:public static <T> List<T> unmodifiableList(List<? extends T> list);用法也很简单,传入一个List实例la,返回这个list

  • 谈谈Java中自定义注解及使用场景

    Java自定义注解一般使用场景为:自定义注解+拦截器或者AOP,使用自定义注解来自己设计框架,使得代码看起来非常优雅.本文将先从自定义注解的基础概念说起,然后开始实战,写小段代码实现自定义注解+拦截器,自定义注解+AOP. 一. 什么是注解(Annotation) Java注解是什么,以下是引用自维基百科的内容 Java注解又称Java标注,是JDK5.0版本开始支持加入源代码的特殊语法元数据. Java语言中的类.方法.变量.参数和包等都可以被标注.和Javadoc不同,Java标注可以通过反

  • Java中JDK动态代理的超详细讲解

    目录 1. 什么是动态代理? 2.动态代理的实现方式有几种? 3. JDK动态代理 4. CGLB动态代理 5.动态代理的效率 6.为什么要使用动态代理呢? 7. JDK动态代理详细使用介绍 总结 1. 什么是动态代理? 动态代理是通过创建代理对象,在不改变原有代码的基础上,给程序增加新的功能,实现了程序的功能增强. 2.动态代理的实现方式有几种? JDK动态代理 CGLB动态代理 3. JDK动态代理 使用了JDK中的InvocationHandler接口,Method类和Proxy类.JDK

  • 全面解析Java中的引用类型

    如果一个内存中的对象没有任何引用的话,就说明这个对象已经不再被使用了,从而可以成为被垃圾回收的候选.不过由于垃圾回收器的运行时间不确定,可被垃圾回收的对象的实际被回收时间是不确定的.对于一个对象来说,只要有引用的存在,它就会一直存在于内存中.如果这样的对象越来越多,超出了JVM中的内存总数,JVM就会抛出OutOfMemory错误.虽然垃圾回收的具体运行是由JVM来控制的,但是开发人员仍然可以在一定程度上与垃圾回收器进行交互,其目的在于更好的帮助垃圾回收器管理好应用的内存.这种交互方式就是使用J

  • java中sdk与jdk的区别详细解析

    SDK是Software Development Kit的缩写,中文意思是"软件开发工具包".这是一个覆盖面相当广泛的名词,可以这么说:辅助开发某一类软件的相关文档.范例和工具的集合都可以叫做"SDK".SDK是一系列文件的组合,它为软件的开发提供一个平台(它为软件开发使用各种API提供便利). JDK(Java Development Kit,Java开发工具包)是Sun Microsystems针对Java开发员的产品.自从Java推出以来,JDK已经成为使用最

随机推荐