专业游戏门户,分享手游网游单机游戏百科知识攻略!

嗨游网
嗨游网

引用类型有哪几种,Java引用类型详解

来源:小嗨整编  作者:小嗨  发布时间:2023-03-02 05:56
摘要:引用类型有哪几种,Java引用类型详解在Java中一切皆对象,对象的操作是通过该对象的引用(Reference)实现的Java中的引用类型有4种,分别是强引用、软引用、弱引用和虚引用(强、软、弱、虚)一、整体架构二、强引用(默认)Java中...

引用类型有哪几种,Java引用类型详解

引用类型有哪几种,Java引用类型详解

在Java中一切皆对象,对象的操作是通过该对象的引用(Reference)实现的

Java中的引用类型有4种,分别是强引用、软引用、弱引用和虚引用(强、软、弱、虚)

一、整体架构

二、强引用(默认)

Java中最常见的就是强引用,在把一个对象赋给一个引用变量时,这个引用变量就是一个强引用(Object obj = new Object(),obj就是一个强引用,obj在栈中,new Object()在堆中)

有强引用的对象一定为可达性状态

即便系统内存非常紧张,Java虚拟机宁愿抛出OutOfMemoryError(OOM)错误,使程序异常终止,也不会回收被强引用所引用的对象

强引用不会被垃圾回收机制直接回收,需要处理,因此强引用是造成内存泄露的主要原因

如果强引用对象不使用时,需要弱化从而使垃圾回收器能够回收,弱化的方式就是给引用变量赋为null,让其超出对象的生命周期范围,则垃圾回收器认为该对象不存在引用,这时就可以回收这个对象,具体什么时候收集取决于垃圾回收器算法

1、使用

public class StronglyReferenceTest {    public static void main(String[] args) throws IOException {        // obj1、obj2存放在栈中        Object obj1 = new Object();        Object obj2 = obj1;        obj1 = null;        System.gc();        // obj1为null被回收,但new Object()还是存放在堆中,被obj2强引用        System.out.println(obj1);    // 不会被垃圾回收        System.out.println(obj2);        // 阻塞main线程,给垃圾回收线程一点时间去执行       System.in.read();    } }

2、补充finalize()

finalize()是Object中的方法,当垃圾回收器将要回收对象所占内存之前被调用,即当一个对象被JVM宣告死亡时会先调用它finalize()方法

Java使用finalize()方法在垃圾回收器将对象从内存中清除出去之前做必要的清理工作

3、注意

(1)全局变量和局部变量

在一个方法的内部有一个强引用,这个引用保存在Java栈中,而真正的引用内容(Object)保存在Java堆中 ,当这个方法运行完成后,就会退出方法栈,则引用对象的引用数为0,这个对象会被回收

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

(2)内存溢出和内存泄漏

内存溢出 OutOfMemory,是指程序在申请内存时,没有足够的内存空间供其使用,出现OutOfMemoryError比如申请了一个Integer,但给它存了long才能存下的数,那就是内存溢出(强引用引发的错误)

内存泄露 MemoryLeak,是指程序在申请内存后,无法释放已申请的内存空间(强引用导致的结果)

大量的内存泄露会导致内存溢出(OOM)

(3)回收问题

对于一个普通的对象,如果没有其他的引用关系,只要超过了引用的作用域或者显示地将相应强引用赋值给null,一般认为就是可以被垃圾回收器收集了,具体还需要看垃圾回收的策略

三、软引用

软引用通过SoftReference类实现,如果一个对象只有软引用,则在系统内存空间不足时该对象将被回收

这一点可以很好地用来解决OOM的问题,并且这个特性很适合用来实现缓存:比如网页缓存、图片缓存等

1、使用

JVM配置:-Xms10M -Xmx25M -XX:+PrintGCDetails

import java.lang.ref.SoftReference; public class SoftReferenceTest {    public static void main(String[] args) {        // 10M        SoftReference<byte[]> softReference = new SoftReference<>(new byte[1024 * 1024 * 10]);        System.out.println(softReference.get());        // 手动回收        System.gc();        try {            Thread.sleep(500);        } catch (InterruptedException e) {            e.printStackTrace();        }        // 执行手动回收后,并没有回收        System.out.println(softReference.get());        // 15M 导致内存不足 自动回收        byte[] b = new byte[1024 * 1024 * 15];        System.out.println(softReference.get());    } }

2、缓存设计思路

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

Map<String, SoftReference<Bitmap>> imageCache = new HashMap<>();

四、弱引用

弱引用通过WeekReference类实现,如果一个对象只有弱引用,则在垃圾回收过程中一定会被回收,也就是不管JVM的内存空间是否足够,都会回收弱引用类型的对象所占用的内存空间

弱引用的生命周期比软引用的生命周期更短

1、使用

import java.lang.ref.WeakReference; public class WeakReferenceTest {    public static void main(String[] args) {        WeakReference<Object> weakReference = new WeakReference<>(new Object());        System.out.println(weakReference.get());        System.gc();       // null        System.out.println(weakReference.get());    } }

2、ThreadLocal

Java里,每个线程(Thread)都有自己的ThreadLocalMap,里边存着自己私有的对象

Map的Entry里,key为ThreadLocal对象,value即为私有对象

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

每个Thread内部都维护一个ThreadLocalMap字典数据结构,字典的Key值是ThreadLocal,那么当某个ThreadLocal对象不再使用(没有其它地方再引用)时,每个已经关联了此ThreadLocal的线程怎么在其内部的ThreadLocalMap里做清除此资源呢?

JDK中的ThreadLocalMap没有继承java.util.Map类,而是自己实现了一套专门用来定时清理无效资源的字典结构

其内部存储实体结构Entry<ThreadLocal, T>继承自java.lan.ref.WeakReference,这样当ThreadLocal不再被引用时,因为弱引用机制原因,当JVM发现内存不足时,会自动回收弱引用指向的实例内存,即其线程内部的ThreadLocalMap会释放其对ThreadLocal的引用从而让JVM回收ThreadLocal对象

这里是重点强调下,是回收对ThreadLocal对象,而非整个Entry,所以线程变量中的值T对象还是在内存中存在的,所以内存泄漏的问题还没有完全解决

调用ThreadLocal.get()或者ThreadLocal.set(T)时都会定期执行回收无效的Entry操作

3、WeakHashMap

import java.util.HashMap; import java.util.Map; import java.util.WeakHashMap; public class WeakHashMapTest {    public static void main(String[] args) {        Map<Integer, String> hashMap =new HashMap<>();        Integer key1 = new Integer("1");        String value1 = "HashMap";        hashMap.put(key1, value1);        System.out.println(hashMap);        key1 = null; // 只跟new Integer("1")有关,跟Map无关系,不影响 HashMap        System.out.println(hashMap);        System.gc(); // 不影响 HashMap        System.out.println(hashMap);        System.out.println("----------");        Map<Integer, String> weakHashMap =new WeakHashMap<>();        Integer key2 = new Integer("2");        String value2 = "WeakHashMap";        weakHashMap.put(key2, value2);        System.out.println(weakHashMap);        key2 = null; // 只跟new Integer("2")有关,跟Map无关系,不影响 WeakHashMap        System.out.println(weakHashMap);        System.gc(); // 影响 WeakHashMap        System.out.println(weakHashMap);    } }

4、ReferenceQueue

import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; public class ReferenceQueueTest {    public static void main(String[] args) {        Object obj = new Object();        ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();        WeakReference<Object> weakReference = new WeakReference<>(obj, referenceQueue);        System.out.println(obj);        System.out.println(weakReference.get());        System.out.println(referenceQueue.poll()); // null        System.out.println("----------");        obj = null;        System.gc();        try {            Thread.sleep(500);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println(obj); // null        System.out.println(weakReference.get()); // null        System.out.println(referenceQueue.poll());    } }

五、虚引用

与其他三种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被回收

PhantomReference的get方法总是返回null,因此无法访问对应的引用对象,它的意义在于说明一个对象已经进入finalization阶段,可以被垃圾回收器回收,用来实现比finalization机制更灵活的回收操作

虚引用通过PhantomReference类实现,虚引用和引用队列联合使用

虚引用主要用来跟踪对象被垃圾回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列(ReferenceQueue)联合使用

当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中

设置虚引用关联的唯一目的就是在这个对象被回收的时候,收到一个系统通知或者后续添加进一步的处理

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

六、总结

当垃圾回收器回收时,某些对象会被回收,某些不会被回收

垃圾回收器会从根对象Object来标记存活的对象,然后将某些不可达的对象和一些引用的对象进行回收

引用类型被垃圾回收时间用途生存时间
强引用从来不会对象的一般状态JVM停止运行时终止
软引用当内存不足时对象缓存内存不足时终止
弱引用正常垃圾回收时对象缓存垃圾回收后终止
虚引用正常垃圾回收时跟踪对象的垃圾回收垃圾回收后终止

本文地址:软件教程频道 https://www.eeeoo.cn/ruanjian/907993.html,嗨游网一个专业手游免费下载攻略知识分享平台,本站部分内容来自网络分享,不对内容负责,如有涉及到您的权益,请联系我们删除,谢谢!


软件教程
小编:小嗨整编
相关文章相关阅读
  • javascript教程网(javascript教程推荐)

    javascript教程网(javascript教程推荐)

    javascript教程网(javascript教程推荐)JavaScript作为一种广泛应用于网页开发的技术,已经成为前端开发不可或缺的一部分。对于刚接触编程的菜鸟来说,选择一份合适的JavaScript教程至关重要。本文将为您推荐几款优...

  • java软件叫什么(java软件安装教程详细)?

    java软件叫什么(java软件安装教程详细)?

    java软件叫什么(java软件安装教程详细)?Java语言是由美国Sun(StanfordUniversityNetwork)公司在1995年推出的,2009年Oracle甲骨文公司收购了Sun公司,如果想开发一个全新的Java程序,必须...

  • 我的世界java版官网(我的世界java版怎么下载)?

    我的世界java版官网(我的世界java版怎么下载)?

    我的世界java版官网(我的世界java版怎么下载)?我的世界java版早在2011年就已发布,而当时的中文补丁使许多人并未购买正版。而如今的网易版让许多人觉得没有童年的味道,那么如何下载java版呢?我的世界java版官网:minecra...

  • 什么是构造函数?详解JavaScript中的构造函数

    什么是构造函数?详解JavaScript中的构造函数

    作为原型和原型链的基础,先了解清楚构造函数以及它的执行过程才能更好地帮助我们学习原型和原型链的知识。本篇文章带大家详细了解一下javascript中的构造函数,介绍一下怎么利用构造函数创建一个js对象,希望对大家有所帮助!一个普通的函数被用...

  • Java 中的各种锁有哪些?

    Java 中的各种锁有哪些?

      Java中15种锁的介绍  在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类。介绍的内容如下:  公平锁/非公平锁  可重入锁/不可重入锁  独享锁/共享锁  互斥锁/读写锁  乐观锁...

  • Java中的Scanner操作详解

    Java中的Scanner操作详解

    scanner是java中的一个常用类,用来读取控制台或文件中的输入数据。它提供了一种简单的方式来解析基本类型和字符串,并支持对正则表达式进行匹配。Scanner类位于java.util包中,因此在编写程序时需要import...

  • java8新特性有哪些

    java8新特性有哪些

    java8新特性有:1、lambda表达式;2、方法引用;3、默认方法;4、新编译工具;5、streamapi;6、datetimeapi;7、option;8、nashornjavascript引擎。Java8新增了非常多的特性...

  • java中tostring方法的作用是什么

    java中tostring方法的作用是什么

    java中tostring方法的作用是会返回一个【以文本方式表示】此对象的字符串,结果是一个简明但易于读懂的信息表达式。java中tostring方法的作用是toString方法会返回一个“以文本方式表示”此对象的字符串。结果是一个简明但易...

  • 周排行
  • 月排行
  • 年排行

精彩推荐