JVM深入学习(十五)-深入了解java引用

简介: 引用是java中堆和栈的桥梁,想要访问堆中的对象,就必须通过引用来访问(8个基本数据类型除外)在垃圾回收中,如果一个对象仍然被GcRoots引用,那么就不会被回收(强引用),这也不是绝对的,主要是根据引用类型来决定的

引用是java中堆和栈的桥梁,想要访问堆中的对象,就必须通过引用来访问(8个基本数据类型除外)

在垃圾回收中,如果一个对象仍然被GcRoots引用,那么就不会被回收(强引用),这也不是绝对的,主要是根据引用类型来决定的

在jvm中也有对于的抽象类 Reference

packagejava.lang.ref;
importjdk.internal.vm.annotation.ForceInline;
importjdk.internal.vm.annotation.IntrinsicCandidate;
importjdk.internal.access.JavaLangRefAccess;
importjdk.internal.access.SharedSecrets;
importjdk.internal.ref.Cleaner;
/*** Abstract base class for reference objects.  This class defines the* operations common to all reference objects.  Because reference objects are* implemented in close cooperation with the garbage collector, this class may* not be subclassed directly.** @author   Mark Reinhold* @since    1.2*/publicabstractclassReference<T> {
    ...
}


并且软/弱/虚引用分别都有对于的实现类

1.1 引用分类

引用类型主要分为四类,四种引用在垃圾回收时表现不同

  1. 强引用 不回收
  2. 软引用 内存不足时回收
  3. 弱引用 发现即回收
  4. 虚引用 对象跟踪回收

由强到虚,回收级别递增.

1.1.1 强引用(StrongReference)

开发过程中的用的基本都是强引用

String str = new String("hello world");

这种最常见的创建对象的方式就是强引用.

这种引用jvm是不会进行回收的,只有当引用被置为null的时候,jvm才会进行回收.

例子证明:

publicclassTest1 {
publicstaticvoidmain(String[] args) {
Stringstr=newString("hello world");
// 垃圾回收System.gc();
// 线程休眠3s,等待gc完成try {
Thread.sleep(3000);
        } catch (InterruptedExceptione) {
e.printStackTrace();
        }
// 如果能打印,就说明没被回收System.out.print(str);
    }
}



一般情况下,出现内存泄漏的问题都是因为强引用.

1.1.2 软引用(SoftReference)

软引用是用来做一些非必要但是还有用的对象. 例如缓存

当内存不足时,软引用会被回收.

具体的逻辑为:

  1. 内存空间不足,进行垃圾回收,回收不可达对象
  2. 不可达对象回收后,内存空间依然不足,进行软引用的回收
  3. 如果软引用回收后,内存空间依然不足,报错OOM,如果内存空间足够,则不报OOM

软引用在jdk中有对应的实现类

packagejava.lang.ref;
/*** Soft reference objects, which are cleared at the discretion of the garbage* collector in response to memory demand.  Soft references are most often used* to implement memory-sensitive caches.** <p> Suppose that the garbage collector determines at a certain point in time* that an object is <a href="package-summary.html#reachability">softly* reachable</a>.  At that time it may choose to clear atomically all soft* references to that object and all soft references to any other* softly-reachable objects from which that object is reachable through a chain* of strong references.  At the same time or at some later time it will* enqueue those newly-cleared soft references that are registered with* reference queues.** <p> All soft references to softly-reachable objects are guaranteed to have* been cleared before the virtual machine throws an* {@code OutOfMemoryError}.  Otherwise no constraints are placed upon the* time at which a soft reference will be cleared or the order in which a set* of such references to different objects will be cleared.  Virtual machine* implementations are, however, encouraged to bias against clearing* recently-created or recently-used soft references.** <p> Direct instances of this class may be used to implement simple caches;* this class or derived subclasses may also be used in larger data structures* to implement more sophisticated caches.  As long as the referent of a soft* reference is strongly reachable, that is, is actually in use, the soft* reference will not be cleared.  Thus a sophisticated cache can, for example,* prevent its most recently used entries from being discarded by keeping* strong referents to those entries, leaving the remaining entries to be* discarded at the discretion of the garbage collector.** @author   Mark Reinhold* @since    1.2*/publicclassSoftReference<T>extendsReference<T> {
...
}


我们用一个例子证明软引用在内存不足时会被回收:

importjava.lang.ref.SoftReference;
publicclassSoftReferenceTest {
publicstaticvoidmain(String[] args) {
// 创建一个软引用 hello_world 关联了一个强引用对象String,当然这个对象创建完就不可达了,会被回收掉,// 此时我们还能否从软引用中获取该对象?SoftReferencehello_world=newSoftReference(newString("hello world"));
// 确定可以通过软引用获取StringSystem.out.println(hello_world.get().toString());
// 接下来设置堆内存大小为 -Xms10M -Xmx10M -XX:+PrintGCDetails// 并触发垃圾回收// 进行异常捕获,最后输出软引用try {
byte[] bytes=newbyte[1024*1024*10];
        }catch (Exceptione){
e.printStackTrace();
        }finally {
System.out.println("垃圾回收后");
System.out.println(hello_world.get());
        }
    }
}



可以看到结果

垃圾回收前后:

此时可以证明: 当内存不足时,会将软引用回收.

注意: 软引用回收指的是,只被软引用关联的对象,如果一个对象既有弱引用,又有强引用,那么是不会被回收的.

1.1.3 弱引用(WeakReference)

弱引用的回收比软引用要快,每次gc的时候都会回收,当然这里的回收也指的是只有弱引用的对象.

这意味着弱引用的生命周期只有一次垃圾回收的长度.

弱引用也有对应的实现类:

packagejava.lang.ref;
/*** Weak reference objects, which do not prevent their referents from being* made finalizable, finalized, and then reclaimed.  Weak references are most* often used to implement canonicalizing mappings.** <p> Suppose that the garbage collector determines at a certain point in time* that an object is <a href="package-summary.html#reachability">weakly* reachable</a>.  At that time it will atomically clear all weak references to* that object and all weak references to any other weakly-reachable objects* from which that object is reachable through a chain of strong and soft* references.  At the same time it will declare all of the formerly* weakly-reachable objects to be finalizable.  At the same time or at some* later time it will enqueue those newly-cleared weak references that are* registered with reference queues.** @author   Mark Reinhold* @since    1.2*/publicclassWeakReference<T>extendsReference<T> {
...
}


弱引用的回收证明例子:

importjava.lang.ref.WeakReference;
importjava.util.WeakHashMap;
publicclassWeakReferenceTest {
publicstaticvoidmain(String[] args) {
WeakReferenceweakReference=newWeakReference<>(newString("hello world"));
System.out.println(weakReference.get());
System.gc();
try {
Thread.sleep(3000);
        } catch (InterruptedExceptione) {
e.printStackTrace();
        }
System.out.println("垃圾回收后");
System.out.println(weakReference.get());
    }
}



结果证明:


1.1.4 虚引用(PhantomReference)

虚引用相比于软/弱引用来说回收的级别更高,也无法根据虚引用来获取对应的对象

对对象来说,有虚引用和没有虚引用是一样的,对对象的生命周期没有任何影响.

虚引用唯一的作用就是来作为对象回收的跟踪,当对象被回收的时候可以通知程序该对象被回收了,所以虚引用的创建必须要指定一个虚引用队列.


虚引用也有对应的实现类

packagejava.lang.ref;
importjdk.internal.vm.annotation.IntrinsicCandidate;
/*** Phantom reference objects, which are enqueued after the collector* determines that their referents may otherwise be reclaimed.  Phantom* references are most often used to schedule post-mortem cleanup actions.** <p> Suppose the garbage collector determines at a certain point in time* that an object is <a href="package-summary.html#reachability">* phantom reachable</a>.  At that time it will atomically clear* all phantom references to that object and all phantom references to* any other phantom-reachable objects from which that object is reachable.* At the same time or at some later time it will enqueue those newly-cleared* phantom references that are registered with reference queues.** <p> In order to ensure that a reclaimable object remains so, the referent of* a phantom reference may not be retrieved: The {@code get} method of a* phantom reference always returns {@code null}.* The {@link #refersTo(Object) refersTo} method can be used to test* whether some object is the referent of a phantom reference.** @author   Mark Reinhold* @since    1.2*/publicclassPhantomReference<T>extendsReference<T> {
...
}



可以通过代码验证虚引用的对象回收通知功能:

importjava.lang.ref.PhantomReference;
importjava.lang.ref.Reference;
importjava.lang.ref.ReferenceQueue;
publicclassPhantomReferenceTest {
staticReferenceQueuereferenceQueue=null;
/*** 守护线程,监听queue队列,当有虚引用被回收时,就可以看到输出哪个对象被回收了*/publicstaticclassCheckGcThreadextendsThread {
@Overridepublicvoidrun() {
if (referenceQueue!=null) {
Referenceremove=null;
try {
remove=referenceQueue.remove();
                } catch (InterruptedExceptione) {
e.printStackTrace();
                }
if (remove!=null) {
System.out.println(remove+"对象被回收了");
                }
            }
        }
    }
publicstaticvoidmain(String[] args) {
// 启动队列监听线程  设置为守护线程,当主线程结束的时候随之结束CheckGcThreadcheckGcThread=newCheckGcThread();
checkGcThread.setDaemon(true);
checkGcThread.start();
// 虚引用的创建必须要传入一个队列referenceQueue=newReferenceQueue();
PhantomReferencehello_world=newPhantomReference(newString("hello"+" world"), referenceQueue);
// 无法通过虚引用获取对象的值System.out.println(hello_world.get());
// 垃圾回收System.gc();
    }
}

可以看到结果:



1.2 扩展

终结器引用: 用于调用对象的finalize方法,也是借助于队列的方式,一般情况用不到

对应实现类:

packagejava.lang.ref;
/*** Final references, used to implement finalization*/classFinalReference<T>extendsReference<T> {
...
}


目录
相关文章
|
26天前
|
监控 算法 Java
Java虚拟机(JVM)的垃圾回收机制深度解析####
本文深入探讨了Java虚拟机(JVM)的垃圾回收机制,旨在揭示其背后的工作原理与优化策略。我们将从垃圾回收的基本概念入手,逐步剖析标记-清除、复制算法、标记-整理等主流垃圾回收算法的原理与实现细节。通过对比不同算法的优缺点及适用场景,为开发者提供优化Java应用性能与内存管理的实践指南。 ####
|
18天前
|
监控 算法 Java
Java虚拟机(JVM)垃圾回收机制深度剖析与优化策略####
本文作为一篇技术性文章,深入探讨了Java虚拟机(JVM)中垃圾回收的工作原理,详细分析了标记-清除、复制算法、标记-压缩及分代收集等主流垃圾回收算法的特点和适用场景。通过实际案例,展示了不同GC(Garbage Collector)算法在应用中的表现差异,并针对大型应用提出了一系列优化策略,包括选择合适的GC算法、调整堆内存大小、并行与并发GC调优等,旨在帮助开发者更好地理解和优化Java应用的性能。 ####
25 0
|
2月前
|
XML Java 编译器
Java学习十六—掌握注解:让编程更简单
Java 注解(Annotation)是一种特殊的语法结构,可以在代码中嵌入元数据。它们不直接影响代码的运行,但可以通过工具和框架提供额外的信息,帮助在编译、部署或运行时进行处理。
97 43
Java学习十六—掌握注解:让编程更简单
|
15天前
|
存储 监控 算法
深入探索Java虚拟机(JVM)的内存管理机制
本文旨在为读者提供对Java虚拟机(JVM)内存管理机制的深入理解。通过详细解析JVM的内存结构、垃圾回收算法以及性能优化策略,本文不仅揭示了Java程序高效运行背后的原理,还为开发者提供了优化应用程序性能的实用技巧。不同于常规摘要仅概述文章大意,本文摘要将简要介绍JVM内存管理的关键点,为读者提供一个清晰的学习路线图。
|
17天前
|
存储 监控 算法
Java虚拟机(JVM)垃圾回收机制深度解析与优化策略####
本文旨在深入探讨Java虚拟机(JVM)的垃圾回收机制,揭示其工作原理、常见算法及参数调优方法。通过剖析垃圾回收的生命周期、内存区域划分以及GC日志分析,为开发者提供一套实用的JVM垃圾回收优化指南,助力提升Java应用的性能与稳定性。 ####
|
21天前
|
机器学习/深度学习 监控 算法
Java虚拟机(JVM)的垃圾回收机制深度剖析####
本文深入探讨Java虚拟机(JVM)的垃圾回收机制,揭示其工作原理、常见算法、性能调优策略及未来趋势。通过实例解析,为开发者提供优化Java应用性能的思路与方法。 ####
31 1
|
21天前
|
Oracle 安全 Java
深入理解Java生态:JDK与JVM的区分与协作
Java作为一种广泛使用的编程语言,其生态中有两个核心组件:JDK(Java Development Kit)和JVM(Java Virtual Machine)。本文将深入探讨这两个组件的区别、联系以及它们在Java开发和运行中的作用。
48 1
|
1月前
|
监控 Java 开发者
Java虚拟机(JVM)深度优化指南####
本文深入探讨了Java虚拟机(JVM)的工作原理及其性能优化策略,旨在帮助开发者通过理解JVM的内部机制来提升Java应用的运行效率。不同于传统的技术教程,本文采用案例分析与实战技巧相结合的方式,为读者揭示JVM调优的艺术。 ####
58 8
|
1月前
|
Java 大数据 API
14天Java基础学习——第1天:Java入门和环境搭建
本文介绍了Java的基础知识,包括Java的简介、历史和应用领域。详细讲解了如何安装JDK并配置环境变量,以及如何使用IntelliJ IDEA创建和运行Java项目。通过示例代码“HelloWorld.java”,展示了从编写到运行的全过程。适合初学者快速入门Java编程。
|
1月前
|
JavaScript Java 项目管理
Java毕设学习 基于SpringBoot + Vue 的医院管理系统 持续给大家寻找Java毕设学习项目(附源码)
基于SpringBoot + Vue的医院管理系统,涵盖医院、患者、挂号、药物、检查、病床、排班管理和数据分析等功能。开发工具为IDEA和HBuilder X,环境需配置jdk8、Node.js14、MySQL8。文末提供源码下载链接。
下一篇
DataWorks