Java 对象四种引用类型

文章目录

  • Java 对象四种引用类型
    • 强引用(Strong Reference)
    • 软引用(Soft Reference)
    • 弱引用(Weak Reference)
    • 虚引用(Phantom Reference)

Java 对象四种引用类型

在 Java 的对象世界里面,对象的引用有 4 类之分,分别是:强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)。

强引用(Strong Reference)

在使用new操作符创建的对象,在对象的生命周期内,GC 是不会回收该对象的,除非主动释放该对象(将引用设置为 null),否则 JVM 宁可抛出 OOM 异常,也不会将具有强引用的对象回收。

如下代码所示,将虚拟机的青年带设置为 10M,老年代设置为 20M,创建两个大对象,大小都为 15 M。

package org.gettingreal.jvm.learning.references;

/**
 * 强引用测试
 */
public class StrongReferenceTest {

    public static void main(String[] args) throws Exception {
        // 大小定位 15 M
        int size = 15 * 1024 * 1024;

        // 创建第一个对象
        byte[] firstBigObject = new byte[size];

        // 创建第二个对象
        byte[] secondBigObject = new byte[size];
    }
  
}

将代码编译后,执行如下命令来观察结果。

# 执行编译
mvn package -DskipTests

# 运行 StrongReferenceTest
java -XX:+PrintGC -Xmn10m -Xms30m -Xmx30m -classpath target/jvm-learnging-1.0.0-SNAPSHOT.jar org.gettingreal.jvm.learning.references.StrongReferenceTest

# 输出
[GC (Allocation Failure)  16195K->15960K(29696K), 0.0012633 secs]
[GC (Allocation Failure)  15960K->15944K(29696K), 0.0007246 secs]
[Full GC (Allocation Failure)  15944K->15672K(29696K), 0.0033753 secs]
[GC (Allocation Failure)  15672K->15672K(29696K), 0.0005597 secs]
[Full GC (Allocation Failure)  15672K->15659K(29696K), 0.0027816 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        at org.gettingreal.jvm.learning.references.StrongReferenceTest.main(StrongReferenceTest.java:16)

可以看出 JVM 执行多次 GC 操作,发现最终无法回收强引用的对象,抛出 OOM 异常。JVM 内部执行的步骤大体如下:

  • 在创建第一个对象时,JVM 尝试在青年代进行分配
  • 此时 JVM 发现青年代内存不够分配,将尝试在老年代进行分配
  • 发现老年代的内存此时够用,将第一个对象分配在老年代
  • 在创建第二个对象时,JVM 还是尝试在青年代进行分配,
  • 此时 JVM 发现青年代内存不够分配,将尝试在老年代进行分配
  • 此时 JVM 发现老年代内存也是不够用,虚拟机发生 GC 操作
  • 发现老年代已分配的内存不能回收(因为第一个对象是强引用),抛出 OOM 异常

软引用(Soft Reference)

软引用表示一个对象有用,但是非必需的,意思是如果一个对象是软引用的,在内存空间充足的情况下,JVM 不会回收该对象。然后在内存空间不足时,JVM 会将该对象回收。

如下代码所示,测试软引用回收的情况,将虚拟机的青年带设置为 10M,老年代设置为 20M,创建两个大对象,大小都为 15 M。

package org.gettingreal.jvm.learning.references;

import java.lang.ref.SoftReference;

/**
 * 软引用测试
 */
public class SoftReferenceTest {

    public static void main(String[] args) throws Exception {
        // 大小定位 15 M
        int size = 15 * 1024 * 1024;

        // 创建第一个对象
        byte[] firstBigObject = new byte[size];

        // 将第一个对象加入到软引用对象中
        SoftReference softReference1 = new SoftReference(firstBigObject);

        // 需要手动释放掉强引用
        firstBigObject = null;


        // 创建第二个对象
        byte[] secondBigObject = new byte[size];

        // 将第一个对象加入到软引用对象中
        SoftReference softReference2 = new SoftReference(secondBigObject);

        // 需要手动释放掉强引用
        secondBigObject = null;
    }
  
}

将代码编译后,执行如下命令来观察结果。

# 执行编译
mvn package -DskipTests

# 运行 SoftReferenceTest
java -XX:+PrintGC -Xmn10m -Xms30m -Xmx30m -classpath target/jvm-learnging-1.0.0-SNAPSHOT.jar org.gettingreal.jvm.learning.references.SoftReferenceTest

# 输出
[GC (Allocation Failure)  16196K->15960K(29696K), 0.0006832 secs]
[GC (Allocation Failure)  15960K->15888K(29696K), 0.0006921 secs]
[Full GC (Allocation Failure)  15888K->15672K(29696K), 0.0031219 secs]
[GC (Allocation Failure)  15672K->15672K(29696K), 0.0003450 secs]
[Full GC (Allocation Failure)  15672K->299K(29696K), 0.0022771 secs]

可以观察到并没有发生 OOM 异常,说明 JVM 回收掉第一个大对象。

可以通过ReferenceQueue来观察引用被回收的情况,只要这个引用被回收,就会将引用添加到此队列中。

package org.gettingreal.jvm.learning.references;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;

/**
 * 软引用队列测试
 */
public class SoftReferenceQueueTest {

    public static void main(String[] args) throws Exception {
        // 大小定位 15 M
        int size = 15 * 1024 * 1024;

        // 定义队列
        ReferenceQueue referenceQueue = new ReferenceQueue();

        try {
            // 创建第一个对象
            byte[] firstBigObject = new byte[size];

            // 将第一个对象加入到软引用对象中
            SoftReference softReference1 = new SoftReference(firstBigObject, referenceQueue);
            System.out.println("添加软引用:" + softReference1);

            // 需要手动释放掉强引用
            firstBigObject = null;


            // 创建第二个对象
            byte[] secondBigObject = new byte[size];

            // 将第一个对象加入到软引用对象中
            SoftReference softReference2 = new SoftReference(secondBigObject, referenceQueue);
            System.out.println("添加软引用:" + softReference2);

            // 需要手动释放掉强引用
            secondBigObject = null;
        } finally {
            SoftReference softReference = null;
            while ((softReference = (SoftReference) referenceQueue.poll()) != null) {
                System.out.println("回收软引用:" + softReference);
            }
        }

    }

}

将代码编译后,执行如下命令来观察结果。

# 执行编译
mvn package -DskipTests

# 运行 SoftReferenceQueueTest
java -Xmn10m -Xms30m -Xmx30m -classpath target/jvm-learnging-1.0.0-SNAPSHOT.jar org.gettingreal.jvm.learning.references.SoftReferenceQueueTest

# 输出
添加软引用:java.lang.ref.SoftReference@3d4eac69
[GC (Allocation Failure)  16197K->15976K(29696K), 0.0009770 secs]
[GC (Allocation Failure)  15976K->15912K(29696K), 0.0005638 secs]
[Full GC (Allocation Failure)  15912K->15674K(29696K), 0.0039520 secs]
[GC (Allocation Failure)  15674K->15674K(29696K), 0.0004479 secs]
[Full GC (Allocation Failure)  15674K->301K(29696K), 0.0026771 secs]
添加软引用:java.lang.ref.SoftReference@42a57993
回收软引用:java.lang.ref.SoftReference@3d4eac69

可以看出,第一个软引用被回收了。

弱引用(Weak Reference)

类似于软引用,但是比软引用还要弱一些,用来描述非必需的对象,只要发生 GC 操作,弱引用都会被回收。

如下代码所示,测试弱引用回收的情况,将虚拟机的青年带设置为 10M,老年代设置为 20M,创建两个大对象,大小都为 15 M。

package org.gettingreal.jvm.learning.references;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;

/**
 * 弱引用测试
 */
public class WeakReferenceTest {

    public static void main(String[] args) throws Exception {
        // 大小定位 15 M
        int size = 15 * 1024 * 1024;

        // 定义队列
        ReferenceQueue referenceQueue = new ReferenceQueue();

        try {
            // 创建第一个对象
            byte[] firstBigObject = new byte[size];

            // 将第一个对象加入到弱引用对象中
            WeakReference weakReference = new WeakReference(firstBigObject, referenceQueue);
            System.out.println("添加弱引用:" + weakReference);

            // 需要手动释放掉强引用
            firstBigObject = null;


            // 创建第二个对象
            byte[] secondBigObject = new byte[size];

            // 将第一个对象加入到弱引用对象中
            WeakReference weakReference2 = new WeakReference(secondBigObject, referenceQueue);
            System.out.println("添加弱引用:" + weakReference2);

            // 需要手动释放掉强引用
            secondBigObject = null;

            // 执行一次 gc 操作
            System.gc();
        } finally {
            WeakReference wr = null;
            while ((wr = (WeakReference) referenceQueue.poll()) != null) {
                System.out.println("回收弱引用:" + wr);
            }
        }

    }

}

将代码编译后,执行如下命令来观察结果。

# 执行编译
mvn package -DskipTests

# 运行 WeakReferenceTest
java -XX:+PrintGC -Xmn10m -Xms30m -Xmx30m -classpath target/jvm-learnging-1.0.0-SNAPSHOT.jar org.gettingreal.jvm.learning.references.WeakReferenceTest

# 输出
添加弱引用:java.lang.ref.WeakReference@3d4eac69
[GC (Allocation Failure)  16197K->15960K(29696K), 0.0008933 secs]
[GC (Allocation Failure)  15960K->15928K(29696K), 0.0008108 secs]
[Full GC (Allocation Failure)  15928K->314K(29696K), 0.0029523 secs]
添加弱引用:java.lang.ref.WeakReference@42a57993
[GC (System.gc())  15838K->15706K(29696K), 0.0004367 secs]
[Full GC (System.gc())  15706K->308K(29696K), 0.0023688 secs]
回收弱引用:java.lang.ref.WeakReference@42a57993
回收弱引用:java.lang.ref.WeakReference@3d4eac69

可以发现,只要发生 GC 操作,弱引用都将会被回收。

虚引用(Phantom Reference)

虚引用实际上是为了资源释放的细粒度控制,但是使用虚引用却需要小心,因为虚引用可能会导致 OOM 的发生。虚引用更倾向于实现程序员对内存回收的细粒度控制,当虚引用会被回收时,向引用系统发出通知,此时可以执行内存的释放相关操作。

虚引用不会主动释放其指向的对象的内存区域,所以当内存满时,会导致 OOM 异常的发生。将虚拟机的青年带设置为 10M,老年代设置为 20M,创建两个大对象,大小都为 15 M。如下代码所示:

package org.gettingreal.jvm.learning.references;

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;

/**
 * 虚引用测试
 */
public class PhantomReferenceTest {

    public static void main(String[] args) {
        int size = 15 * 1024 * 1024;
        ReferenceQueue referenceQueue = null;

        try {
            // 定义队列
            referenceQueue = new ReferenceQueue();

            // 将第一个对象加入到软虚用对象中
            PhantomReference phantomReference1 = new PhantomReference(new byte[size], referenceQueue);
            System.out.println("添加虚引用,phantomReference1:" + phantomReference1);

            // 将第一个对象加入到软引用对象中
            PhantomReference phantomReference2 = new PhantomReference(new byte[size], referenceQueue);
            System.out.println("添加虚引用,phantomReference2:" + phantomReference2);

            System.gc();
        } finally {
            System.out.println("\n检查回收虚引用:");
            PhantomReference pr = null;
            while ((pr = (PhantomReference) referenceQueue.poll()) != null) {
                System.out.println("回收虚引用:" + pr);
            }
        }

    }

}

将代码编译后,执行如下命令来观察结果。

# 执行编译
mvn package -DskipTests

# 运行 PhantomReferenceTest
java -XX:+PrintGC -Xmn10m -Xms30m -Xmx30m -classpath target/jvm-learnging-1.0.0-SNAPSHOT.jar org.gettingreal.jvm.learning.references.PhantomReferenceTest

# 输出
添加虚引用,phantomReference1:java.lang.ref.PhantomReference@3d4eac69
[GC (Allocation Failure)  16197K->15960K(29696K), 0.0006786 secs]
[GC (Allocation Failure)  15960K->15896K(29696K), 0.0007171 secs]
[Full GC (Allocation Failure)  15896K->15674K(29696K), 0.0032693 secs]
[GC (Allocation Failure)  15674K->15674K(29696K), 0.0005009 secs]
[Full GC (Allocation Failure)  15674K->15661K(29696K), 0.0024825 secs]

检查回收虚引用:
回收虚引用:java.lang.ref.PhantomReference@3d4eac69
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        at org.gettingreal.jvm.learning.references.PhantomReferenceTest.main(PhantomReferenceTest.java:24)

可以看出并没有释放 phantomReference1对应的内存。可以通过手动调用 PhantomReference 实例的 clear() 方法来释放对应的内存。

package org.gettingreal.jvm.learning.references;

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;

/**
 * 虚引用清理测试
 */
public class PhantomReferenceCleanTest {

    public static void main(String[] args) throws Exception {
        int size = 15 * 1024 * 1024;
        ReferenceQueue referenceQueue = null;

        try {
            // 定义队列
            referenceQueue = new ReferenceQueue();

            // 将第一个对象加入到软虚用对象中
            PhantomReference phantomReference = new PhantomReference(new byte[size], referenceQueue);
            System.out.println("添加虚引用,phantomReference1:" + phantomReference);

            System.gc();
        } finally {
            System.out.println("\n检查回收虚引用:");
            PhantomReference pr = null;
            while ((pr = (PhantomReference) referenceQueue.poll()) != null) {
                System.out.println("回收虚引用:" + pr);

                // 调用 clear() 方法,模拟内存释放的细粒度操作
                pr.clear();
            }
        }

        try {
            Thread.sleep(3000);
            // 将第二个对象加入到软虚用对象中
            PhantomReference phantomReference2 = new PhantomReference(new byte[size], referenceQueue);
            System.out.println("添加虚引用,phantomReference2:" + phantomReference2);

            System.gc();
        } finally {
            System.out.println("\n检查回收虚引用2:");
            PhantomReference pr = null;
            while ((pr = (PhantomReference) referenceQueue.poll()) != null) {
                System.out.println("回收虚引用:" + pr);

                // 调用 clear() 方法,模拟内存释放的细粒度操作
                pr.clear();
            }
        }

    }

}

将代码编译后,执行如下命令来观察结果。

# 执行编译
mvn package -DskipTests

# 运行 PhantomReferenceCleanTest
java -XX:+PrintGC -Xmn10m -Xms30m -Xmx30m -classpath target/jvm-learnging-1.0.0-SNAPSHOT.jar org.gettingreal.jvm.learning.references.PhantomReferenceCleanTest

# 输出
添加虚引用,phantomReference1:java.lang.ref.PhantomReference@3d4eac69
[GC (System.gc())  16197K->15992K(29696K), 0.0009614 secs]
[Full GC (System.gc())  15992K->15675K(29696K), 0.0032103 secs]

检查回收虚引用:
回收虚引用:java.lang.ref.PhantomReference@3d4eac69
[GC (Allocation Failure)  15839K->15739K(29696K), 0.0012892 secs]
[GC (Allocation Failure)  15739K->15771K(29696K), 0.0007469 secs]
[Full GC (Allocation Failure)  15771K->308K(29696K), 0.0042175 secs]
添加虚引用,phantomReference2:java.lang.ref.PhantomReference@42a57993
[GC (System.gc())  15832K->15700K(29696K), 0.0004901 secs]
[Full GC (System.gc())  15700K->15669K(29696K), 0.0018866 secs]

检查回收虚引用2:
回收虚引用:java.lang.ref.PhantomReference@42a57993

可以发现,phantomReference1 在 GC 操作时,加入到了 referenceQueue 队列,在遍历队列时,调用引用的 clean() 方法来执行对象对应内存的释放。再次添加一个对象时,不会出现 OOM 异常。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/13075.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Session使用和原理分析图与实现原理-- 代码演示说明 Session 的生命周期和读取的机制代码分析

目录 Web 开发会话技术 -Session —session 技术 session 基本原理 Session 可以做什么 如何理解 Session Session 的基本使用 session 底层实现机制 原理分析图 代码演示 CreateSession.java 测试 Session 创的机制, 注意抓包分析​编辑 ReadSession.j…

python+vue 基于推荐算法的在线电影视播放网站

以广大影视剧迷们为研究对象,深入了解影视剧迷对在线视频观看视频的需求进行分析,形成系统需求分析设计一个符合影视剧迷们需求的在线视频网站。设计网站的前期工作包括对系统的各个功能进行详细分析,对数据库设计进行详细的描述,…

【C++】STL理解【容器】

【C】STL理解【容器】 1. STL概念引入 长久以来,软件界一直希望建立一种可重复利用的东西,以及一种得以制造出”可重复运用的东西”的方法,从函数(functions),类别(classes),函数库(function libraries),类别库(class libraries…

nssctf web 入门(6)

这里通过nssctf的题单web安全入门来写,会按照题单详细解释每题。题单在NSSCTF中。 想入门ctfweb的可以看这个系列,之后会一直出这个题单的解析,题目一共有28题,打算写10篇。 目录 [SWPUCTF 2021 新生赛]caidao [SWPUCTF 2021 新…

GitLab与jekins结合构建持续集成(cl)环境(2)

目录 GItlab配置邮箱 绑定邮箱 创建群组 添加人员 创建一个项目 添加文件 新建分支 如何拉取代码 Git bash 演示 Git GUI演示 安装jenkins 更改插件镜像源 配置jenkins使用gitlab更新代码 安装jekins插件 配置jenkins免密拉取gatlab代码 jenkins创建项目 将代码…

VUE基本使用详解

目录 一、VUE框架原理 1. 了解VUE框架 2. VUE框架原理 3. MVC设计模式 4. MVVM设计模式 二、引入VUE框架 1. 本地引入 2. 网络引入 三、安装Vue插件 一、VUE框架原理 1. 了解VUE框架 vue 框架 是基于MVVM设计模式的前端框架,其中的Vue对象是MVVM设计模式中的VM视图…

Zebec Protocol 出席香港 Web3 峰会,带来了哪些信息?

梳理香港加密新政的细节,一个明确的脉络是,香港加密新政的整体目的是令虚拟资产交易明确化和合法化,通过不断完善的监管框架,促进香港虚拟资产行业的可持续和负责任地发展。 在加强合规和持牌经营的监管思路下,长期审慎…

TensorFlow 和 Keras 应用开发入门:1~4 全

原文:Beginning Application Development with TensorFlow and Keras 协议:CC BY-NC-SA 4.0 译者:飞龙 本文来自【ApacheCN 深度学习 译文集】,采用译后编辑(MTPE)流程来尽可能提升效率。 不要担心自己的形…

《简化iOS APP上架流程,App Uploader助你搞定!》

转载:Appuploader常见问题 Appuploader 常见错误及解决方法 问题解决秘籍 遇到问题,第一个请登录苹果开发者官网 检查一遍账号是否有权限,是否被停用,是否过期,是否有协议需要同意,并且在右上角切换账号后…

页表结构详细说明

一、页表 1. 内存地址的分解 我们知道linux采用了分页机制,通常采用四级页表,页全局目录(PGD),页上级目录(PUD),页中间目录(PMD),页表(PTE)。如下: 其含义定义在arch/arm64/include/asm/pgtable-hwdef.…

HCIP-6.9BGP路由反射器原理与配置

路由反射器原理与配置 1、路由反射器概念1.1、路由反射器原理:1.2、多集群路由反射器1.3、备份路由反射器2、路由反射器配置3、路由反射器防环机制 1、路由反射器概念 IBGP的水平分割,IBGP 1只能update一跳,就是说在IBGP 2 设备收到IBGP 1设…

密码学|DES加密算法|学习记录

DES简介 DES属于对称密码算法中的分组加密算法 密钥一共64bit,其中56位参与运算,其余8bit为校验位(8 16 24 32 40 48 56 64) n个64位明块经过加密后得到的n个64位密文块加在一起就是密文 DES一般步骤 IP置换 : IP置…

Python中的异常——概述和基本语法

Python中的异常——概述和基本语法 摘要:Python中的异常是指在程序运行时发生的错误情况,包括但不限于除数为0、访问未定义变量、数据类型错误等。异常处理机制是Python提供的一种解决这些错误的方法,我们可以使用try/except语句来捕获异常并…

AI已经解锁自动化能力 | 颠覆商业模式和劳动力市场

AI已经解锁自动化能力,将颠覆商业模式和劳动力市场。目前AutoGPT的开源项目: BabyAGI、Auto-GPT、AgentGPT、TeenagerAGI、Jarvis。 AutoGPT原理: 3个GPT4协同合作,一个GPT4负责分解目标创建任务,另一个GPT4负责分配…

C# switch case语句入门and业务必知点

具体的语法形式如下。 switch(表达式) { case 值 1: 语句块 1; break; case 值 2: 语句块 2; break; ... default: 语句块 n; break; } 在这里,switch 语句中表达式的结果必须是整型、字符串…

Linux用户的分类与家目录,ls、pwd、cd、mkdir、touch、rmdir、rm指令与选项等

Linux中用户的分类与用户的家目录 在Linux当中,用户的分类只分为两类,一类叫做超级用户root,还有就是其他也就是传说中的普通用户。我们刚刚登进去时,默认所处的目录是***/root或者/home/用户名***,比如说/root, /hom…

Unity InputSystem (一)

什么是InputSystem InputSystem 是 2019 年 Unity 新推出的插件,输入系统包实现了一个系统来使用任何类型的输入设备来控制你的 Unity 内容。它旨在成为 Unity 的经典输入管理器更强大、更灵活、更可配置的替代品。 新输入系统基于事件,输入设备和动作逻…

信安大佬真的用kali吗?

Kali只是现在网络安全和kali比较火的一个操作系统 下面我为大家讲讲kali系统都有那些优点 Kali介绍Kali Linux是基于Debian的Linux发行版, 设计用于数字取证操作系统。面向专业的渗透测试和安全审计。 集成化:预装超过300个渗透测试工具兼容好&#x…

【JavaEE】File、InputStream和OutputStream的使用

1.File 在计算机中目录结构如下: 而File就表示一个目录或者一个普通文件。 File表示目录: File表示普通文件: 我们先来看File的构造方法: 构造器描述File(File parent, String child)根据父目录 孩子文件路径,创…

ROS学习第九节——服务通信

1.基本介绍 服务通信较之于话题通信更简单些,理论模型如下图所示,该模型中涉及到三个角色: ROS master(管理者)Server(服务端)Client(客户端) ROS Master 负责保管 Server 和 Client 注册的信息,并匹配话题相同的 Server 与 Client &#…
最新文章