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> {
...
}


目录
相关文章
|
3月前
|
IDE Java 编译器
java编程最基础学习
Java入门需掌握:环境搭建、基础语法、面向对象、数组集合与异常处理。通过实践编写简单程序,逐步深入学习,打牢编程基础。
249 1
|
3月前
|
存储 Oracle Java
java零基础学习者入门课程
本课程为Java零基础入门教程,涵盖环境搭建、变量、运算符、条件循环、数组及面向对象基础,每讲配示例代码与实践建议,助你循序渐进掌握核心知识,轻松迈入Java编程世界。
349 0
|
4月前
|
Java API 容器
Java基础学习day08-2
本节讲解Java方法引用与常用API,包括静态、实例、特定类型方法及构造器引用的格式与使用场景,并结合代码示例深入解析。同时介绍String和ArrayList的核心方法及其实际应用。
170 1
|
3月前
|
负载均衡 Java API
grpc-java 架构学习指南
本指南系统解析 grpc-java 架构,涵盖分层设计、核心流程与源码结构,结合实战路径与调试技巧,助你从入门到精通,掌握高性能 RPC 开发精髓。
369 7
|
4月前
|
Java
Java基础学习day08-作业
本作业涵盖Java中Lambda表达式的应用,包括Runnable与Comparator接口的简化实现、自定义函数式接口NumberProcessor进行加减乘及最大值操作,以及通过IntProcessor处理整数数组,实现遍历、平方和奇偶判断等功能,强化函数式编程实践。
89 5
|
4月前
|
Java 程序员
Java基础学习day08
本节讲解Java中的代码块(静态与实例)及其作用,深入介绍内部类(成员、静态、局部及匿名)的定义与使用,并引入函数式编程思想,重点阐述Lambda表达式及其在简化匿名内部类中的应用。
164 5
|
4月前
|
Java
Java基础学习day07-作业
本作业包含六个Java编程案例:1)动物类继承与多态;2)加油卡支付系统;3)员工管理类设计;4)学生信息统计接口;5)USB设备控制;6)家电智能控制。综合运用抽象类、接口、继承、多态等面向对象技术,强化Java基础编程能力。
201 3
|
8月前
|
Arthas 存储 算法
深入理解JVM,包含字节码文件,内存结构,垃圾回收,类的声明周期,类加载器
JVM全称是Java Virtual Machine-Java虚拟机JVM作用:本质上是一个运行在计算机上的程序,职责是运行Java字节码文件,编译为机器码交由计算机运行类的生命周期概述:类的生命周期描述了一个类加载,使用,卸载的整个过类的生命周期阶段:类的声明周期主要分为五个阶段:加载->连接->初始化->使用->卸载,其中连接中分为三个小阶段验证->准备->解析类加载器的定义:JVM提供类加载器给Java程序去获取类和接口字节码数据类加载器的作用:类加载器接受字节码文件。
768 55
|
3月前
|
存储 缓存 Java
我们来说一说 JVM 的内存模型
我是小假 期待与你的下一次相遇 ~
314 5
|
3月前
|
存储 缓存 算法
深入理解JVM《JVM内存区域详解 - 世界的基石》
Java代码从编译到执行需经javac编译为.class字节码,再由JVM加载运行。JVM内存分为线程私有(程序计数器、虚拟机栈、本地方法栈)和线程共享(堆、方法区)区域,其中堆是GC主战场,方法区在JDK 8+演变为使用本地内存的元空间,直接内存则用于提升NIO性能,但可能引发OOM。