【Android 内存优化】Java 引用类型 ( 强引用 | 软引用 | 弱引用 | 虚引用 )

简介: 【Android 内存优化】Java 引用类型 ( 强引用 | 软引用 | 弱引用 | 虚引用 )

文章目录

一、 Java 引用类型 ( 强、软、弱、虚 )

二、 软引用代码示例

三、 弱引用代码示例



强引用 ( 不回收 ) > 软引用 ( OOM 前回收 ) > 弱引用 ( GC 必回收 ) > 虚引用 ( 回收前通知 )


如果单纯的想避免 OOM , 可以使用软引用 ;


如果对性能要求很高 , 要最大限度节省内存 , 所有的内存回收都要及时处理 , 就使用弱引用 ;


内存泄漏原理 : 长生命周期对象 , 持有短生命周期对象的引用 , 并且是强引用持有 , GC 无法释放该短生命周期对象引用 , 造成 OOM ;






一、 Java 引用类型 ( 强、软、弱、虚 )


1. 引用 :


① 变量引用 : 创建对象 , 将对象赋值给变量 V VV , 那么变量 V VV 就是对象的引用 ;


② 对象引用 : 对象 A AA 可以调用对象 B BB , 那么对象 A AA 持有对象 B BB 的引用 ;




2. 引用类型 : GC 垃圾回收机制 与 引用类型 密切相关 ;



① 强引用 : 最普遍的引用 , Object object = new Object() , 这就是强引用 ;


② 软引用 : 用于定义一些有用 , 但不是必须的对象 , 对象被软引用引用 , 当系统内存严重不足 , 在报出 OutOfMemoryError 错误之前就会将软件用释放掉 , 如果将软引用释放掉之后 , 还是内存不足 , 就会报 OutOfMemoryError 异常 ;


③ 弱引用 : 弱引用也是描述非必须对象 , 该引用的引用强度比软引用更弱 , 该引用对象 , 生命周期只到下一次 GC 回收之前 , GC 只要扫描到了弱引用 , 直接回收 , 即使内存足够 , 也要回收该对象所占用的内存 ;


④ 虚引用 : 虚引用无法获得被引用的对象 , 其唯一的作用是对象被回收时 , 可以得到相应的通知 ; 虚引用不会对对象的生存周期造成影响任何 , GC 忽略虚引用 , 即使有虚引用引用某对象 , GC 会当做该引用不存在 ; 开发时不常用 , 一般开发底层 SDK , 或监测系统运行类的软件时 , 才使用 ;



强引用 ( 不回收 ) > 软引用 ( OOM 前回收 ) > 弱引用 ( GC 必回收 ) > 虚引用 ( 回收前通知 )






二、 软引用代码示例


1. 软引用创建 :



① 直接创建软引用 : 在软件用构造函数中创建一个软引用 , 不涉及外部引用 ;


SoftReference<Object> softReference3 = new SoftReference<>(new Object());

② 传入创建好的对象引用创建软引用 : 软引用是通过在 软引用构造函数 传入引用对象创建的 , 首先要创建引用对象 ; 下面的强引用可以在创建软引用完毕后置空解除引用 , 否则肯定不能被回收 ;


Object softObject = new Object();
SoftReference<Object> softReference1 = new SoftReference<>(softObject);


③ 创建软引用并设置引用队列 : 在上述基础上 , 可以在构造函数中设置一个引用队列参数 ;


Object softObject = new Object();
// 引用队列
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
// 创建软引用, 并将该软引用引用放入引用队列中
SoftReference<Object> softReference2 = new SoftReference<>(softObject, referenceQueue);


④ 上述软引用释放 : 创建的软引用在两种情况下才会释放 :


软引用置空 : GC Root 不可达时回收 ;

内存不足 : 内存不足 , OOM 之前回收 ;



2. 软引用代码示例 :


import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
public class Main {
    public static void main(String[] args) throws InterruptedException {
        /*
            软引用示例
            1. 创建软引用 : public SoftReference(T referent)
            2. 创建软引用并放入引用队列中 :
                public SoftReference(T referent, ReferenceQueue<? super T> q)
            回收时机 : 软引用在内存不足时才会被回收, OOM 的前一刻
         */
        Object softObject = new Object();
        // 1. 直接创建软引用
        SoftReference<Object> softReference1 = new SoftReference<>(softObject);
        // 2. 创建软引用并放入引用队列中
        // 引用队列
        ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
        // 创建软引用, 并将该软引用引用放入引用队列中
        SoftReference<Object> softReference2 = new SoftReference<>(softObject, referenceQueue);
        // 3. 直接创建软件用
        SoftReference<Object> softReference3 = new SoftReference<>(new Object());
        //获取软引用, 调用软引用的 get() 方法 , 即可获取软引用
        softReference1.get();
        softReference2.get();
        // 打印软件用
        System.out.println("softReference1 : " + softReference1.get());
        System.out.println("softReference2 : " + softReference2.get());
        System.out.println("softReference3 : " + softReference3.get());
        // 软件用被回收之后, 可以通过调用 referenceQueue.poll() 方法, 获取到被回收的引用
        // 如果获取到 null, 说明该软引用没有被回收
        // 如果该软件用被回收, 可以获取非空值
        System.out.println("referenceQueue.poll : " + referenceQueue.poll());
        // 验证回收机制, 将软引用对象设置为空
        // 其对应的软引用对象 SoftReference<Object> 回收的时机
        //  - 1. 置空访问不可达时回收
        //  - 2. 内存不足 OOM 之前回收
        softObject = null;
        // 申请进行垃圾回收, 这里只是申请垃圾回收, 并不会马上调用, 大约几秒内会进行垃圾回收
        System.gc();
        Thread.sleep(5_000);
        // 打印相关变量验证回收机制
        System.out.println("");
        System.out.println("softReference1 : " + softReference1.get());
        System.out.println("softReference2 : " + softReference2.get());
        System.out.println("softReference3 : " + softReference3.get());
        // 软件用被回收之后, 可以通过调用 referenceQueue.poll() 方法, 获取到被回收的引用
        // 如果获取到 null, 说明该软引用没有被回收
        // 如果该软件用被回收, 可以获取非空值
        System.out.println("referenceQueue.poll : " + referenceQueue.poll());
    }
}


执行结果 :


softReference1 : java.lang.Object@1b6d3586
softReference2 : java.lang.Object@1b6d3586
softReference3 : java.lang.Object@4554617c
referenceQueue.poll : null
softReference1 : java.lang.Object@1b6d3586
softReference2 : java.lang.Object@1b6d3586
softReference3 : java.lang.Object@4554617c
referenceQueue.poll : null




三、 弱引用代码示例


1. 弱引用创建 :



① 直接创建弱引用 : 在软件用构造函数中创建一个软引用 , 不涉及外部引用 ;


WeakReference<Object> weakReference3 = new WeakReference<>(new Object());


② 传入创建好的对象引用创建弱引用 : 弱引用是通过在 弱引用构造函数 传入引用对象创建的 , 首先要创建引用对象 ; 下面的强引用可以在创建软引用完毕后置空解除引用 , 否则肯定不能被回收 ;


Object softObject = new Object();
WeakReference<Object> WeakReference1 = new WeakReference<>(weakObject);


③ 创建弱引用并设置引用队列 : 在上述基础上 , 可以在构造函数中设置一个引用队列参数 ;


Object softObject = new Object();
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
// 创建弱引用, 并将该弱引用引用放入引用队列中
WeakReference<Object> weakReference2 = new WeakReference<>(weakObject, referenceQueue);


④ 上述软引用释放 : 弱引用不管是否置空 , 只要遇到 GC , 一律回收




2. 软引用代码示例 :


import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
public class Main {
    public static void main(String[] args) throws InterruptedException {
        /*
            弱引用示例
            1. 创建弱引用 : public WeakReference(T referent)
            2. 创建弱引用并放入引用队列中 :
                public WeakReference(T referent, ReferenceQueue<? super T> q)
            回收时机 : 弱引用在 GC 时被回收, 不管内存是否足够
         */
        Object weakObject = new Object();
        // 1. 直接创建弱引用
        WeakReference<Object> WeakReference1 = new WeakReference<>(weakObject);
        // 2. 创建弱引用并放入引用队列中
        // 引用队列
        ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
        // 创建弱引用, 并将该弱引用引用放入引用队列中
        WeakReference<Object> weakReference2 = new WeakReference<>(weakObject, referenceQueue);
        // 3. 直接创建弱引用
        WeakReference<Object> weakReference3 = new WeakReference<>(new Object());
        //获取弱引用, 调用弱引用的 get() 方法 , 即可获取弱引用
        WeakReference1.get();
        weakReference2.get();
        // 打印软件用
        System.out.println("WeakReference1 : " + WeakReference1.get());
        System.out.println("WeakReference2 : " + weakReference2.get());
        System.out.println("WeakReference3 : " + weakReference3.get());
        // 弱引用被回收之后, 可以通过调用 referenceQueue.poll() 方法, 获取到被回收的引用
        // 如果获取到 null, 说明该弱引用没有被回收
        // 如果该软件用被回收, 可以获取非空值
        System.out.println("referenceQueue.poll : " + referenceQueue.poll());
        // 验证回收机制, 将弱引用对象设置为空
        // 这里弱引用不管是否置空 , 只要遇到 GC , 一律回收
        weakObject = null;
        // 申请进行垃圾回收, 这里只是申请垃圾回收, 并不会马上调用, 大约几秒内会进行垃圾回收
        System.gc();
        Thread.sleep(5_000);
        // 打印相关变量验证回收机制
        System.out.println("");
        System.out.println("WeakReference1 : " + WeakReference1.get());
        System.out.println("WeakReference2 : " + weakReference2.get());
        System.out.println("WeakReference3 : " + weakReference3.get());
        // 软件用被回收之后, 可以通过调用 referenceQueue.poll() 方法, 获取到被回收的引用
        // 如果获取到 null, 说明该弱引用没有被回收
        // 如果该软件用被回收, 可以获取非空值
        System.out.println("referenceQueue.poll : " + referenceQueue.poll());
    }
}



执行结果 :


WeakReference1 : java.lang.Object@1b6d3586
WeakReference2 : java.lang.Object@1b6d3586
WeakReference3 : java.lang.Object@4554617c
referenceQueue.poll : null
WeakReference1 : null
WeakReference2 : null
WeakReference3 : null
referenceQueue.poll : java.lang.ref.WeakReference@74a14482


目录
相关文章
|
3月前
|
存储 开发框架 .NET
"揭秘.NET内存奥秘:从CIL深处窥探值类型与引用类型的生死较量,一场关于速度与空间的激情大戏!"
【8月更文挑战第16天】在.NET框架中,通过CIL(公共中间语言)可以深入了解值类型与引用类型的内存分配机制。值类型如`int`和`double`直接在方法调用堆栈上分配,访问迅速,生命周期随栈帧销毁而结束。引用类型如`string`在托管堆上分配,堆栈上仅存储引用,CLR负责垃圾回收,确保高效且自动化的内存管理。
56 6
|
3月前
|
存储 Java 开发工具
【Azure 存储服务】Azure Blob上传大文件(600MB)出现内存溢出情况(Java SDK)
【Azure 存储服务】Azure Blob上传大文件(600MB)出现内存溢出情况(Java SDK)
|
1月前
|
Java 测试技术 Android开发
让星星⭐月亮告诉你,强软弱虚引用类型对象在内存足够和内存不足的情况下,面对System.gc()时,被回收情况如何?
本文介绍了Java中四种引用类型(强引用、软引用、弱引用、虚引用)的特点及行为,并通过示例代码展示了在内存充足和不足情况下这些引用类型的不同表现。文中提供了详细的测试方法和步骤,帮助理解不同引用类型在垃圾回收机制中的作用。测试环境为Eclipse + JDK1.8,需配置JVM运行参数以限制内存使用。
32 2
|
2月前
|
Java Android开发 C++
🚀Android NDK开发实战!Java与C++混合编程,打造极致性能体验!📊
在Android应用开发中,追求卓越性能是不变的主题。本文介绍如何利用Android NDK(Native Development Kit)结合Java与C++进行混合编程,提升应用性能。从环境搭建到JNI接口设计,再到实战示例,全面展示NDK的优势与应用技巧,助你打造高性能应用。通过具体案例,如计算斐波那契数列,详细讲解Java与C++的协作流程,帮助开发者掌握NDK开发精髓,实现高效计算与硬件交互。
137 1
|
2月前
|
安全 Java API
【性能与安全的双重飞跃】JDK 22外部函数与内存API:JNI的继任者,引领Java新潮流!
【9月更文挑战第7天】JDK 22外部函数与内存API的发布,标志着Java在性能与安全性方面实现了双重飞跃。作为JNI的继任者,这一新特性不仅简化了Java与本地代码的交互过程,还提升了程序的性能和安全性。我们有理由相信,在外部函数与内存API的引领下,Java将开启一个全新的编程时代,为开发者们带来更加高效、更加安全的编程体验。让我们共同期待Java在未来的辉煌成就!
65 11
|
3月前
|
存储 搜索推荐 Java
探索安卓开发中的自定义视图:打造个性化UI组件Java中的异常处理:从基础到高级
【8月更文挑战第29天】在安卓应用的海洋中,一个独特的用户界面(UI)能让应用脱颖而出。自定义视图是实现这一目标的强大工具。本文将通过一个简单的自定义计数器视图示例,展示如何从零开始创建一个具有独特风格和功能的安卓UI组件,并讨论在此过程中涉及的设计原则、性能优化和兼容性问题。准备好让你的应用与众不同了吗?让我们开始吧!
|
3月前
|
Java 调度 Android开发
Android经典实战之Kotlin的delay函数和Java中的Thread.sleep有什么不同?
本文介绍了 Kotlin 中的 `delay` 函数与 Java 中 `Thread.sleep` 方法的区别。两者均可暂停代码执行,但 `delay` 适用于协程,非阻塞且高效;`Thread.sleep` 则阻塞当前线程。理解这些差异有助于提高程序效率与可读性。
76 1
|
3月前
|
Java Android开发
解决Android编译报错:Unable to make field private final java.lang.String java.io.File.path accessible
解决Android编译报错:Unable to make field private final java.lang.String java.io.File.path accessible
492 1
|
3月前
|
Android开发
Cannot create android app from an archive...containing both DEX and Java-bytecode content
Cannot create android app from an archive...containing both DEX and Java-bytecode content
35 2
下一篇
无影云桌面