GC(通过Reference和GC交互)

本文介绍了Java中不同类型的引用(如SoftReference、WeakReference和PhantomReference)及其应用场景,探讨了如何利用这些引用与垃圾回收器(GC)进行交互以提高程序性能。此外,还提供了若干关于内存管理和GC优化的有效建议。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

相关文章:
1.安全点的相关知识
http://blog.youkuaiyun.com/youyou1543724847/article/details/52728148
1.1 OOPMap
http://blog.youkuaiyun.com/youyou1543724847/article/details/52728154
2. GC基本算法
http://blog.youkuaiyun.com/youyou1543724847/article/details/52728210
3. G1算法
http://blog.youkuaiyun.com/youyou1543724847/article/details/52728244
4.通过Reference和GC交互
http://blog.youkuaiyun.com/youyou1543724847/article/details/52728290
5.GC友好编程
http://blog.youkuaiyun.com/youyou1543724847/article/details/52728301
6.其他
http://blog.youkuaiyun.com/youyou1543724847/article/details/52733325

##Java中相关概念
ReferenceQueue
WeakReference
SoftReference
PhantomReference
WeakHashMap

Java2 增强了内存管理功能,增加了一个java .lang.ref 包,其中定义了三种引用类。这三种引用类分别为SoftReference、 WeakReference和 PhantomReference.通过使用这些引用类,程序员可以在一定程度与GC进行交互,以便改善GC 的工作效率。这些引用类的引用强度介于可达对象和不可达对象之间。

创建一个引用对象也非常容易,例如如果你需要创建一个Soft Reference对象,那么首先创建一个对象,并采用普通引用方式(可达对象);然后再创建一个SoftReference引用该对象;最后将普通引用设置为null.通过这种方式,这个对象就只有一个Soft Reference引用。同时,我们称这个对象为Soft Reference对象。

Soft Reference的主要特点是据有较强的引用功能。

只有当内存不够的时候,才进行回收这类内存,因此在内存足够的时候,它们通常不被回收。另外,这些引用对象还能保证在Java 抛出OutOfMemory 异常之前,被设置为null.它可以用于实现一些常用图片的缓存,实现Cache的功能,保证最大限度的使用内存而不引起OutOfMemory.以下给 出这种引用类型的使用伪代码;
这里写图片描述

软引用是主要用于内存敏感的高速缓存。在jvm报告内存不足之前会清除所有的软引用,这样以来gc就有可能收集软可及的对象,可能解决内存吃紧问题,避免内存溢出。什么时候会被收集取决于gc的算法和gc运行时可用内存的大小。当gc决定要收集软引用是执行以下过程,以上面的abcSoftRef为例:
1 首先将abcSoftRef的referent设置为null,不再引用heap中的new String(“abc”)对象。
2 将heap中的new String(“abc”)对象设置为可结束的(finalizable)。
3 当heap中的new String(“abc”)对象的finalize()方法被运行而且该对象占用的内存被释放, abcSoftRef被添加到它的ReferenceQueue中。

注:对ReferenceQueue软引用和弱引用可以有可无,但是虚引用必须有,参见:

Java代码
Reference(T paramT, ReferenceQueue

Phantom 引用

Phantom 引用的用途较少.主要用于辅助finalize函数的使用。Phantom对象指一些对象,它们执行完了finalize函数,并为不可达对象,但是它们 还没有被GC 回收。这种对象可以辅助finalize进行一些后期的回收工作,我们通过覆盖 Reference的clear()方法,增强资源回收机制的灵活性。

当GC一但发现了虚引用对象,将会将PhantomReference对象插入ReferenceQueue队列,而此时PhantomReference所指向的对象并没有被GC回收,而是要等到ReferenceQueue被你真正的处理后才会被回收。虚引用的用法:
这里写图片描述

看了简单的定义之后,我们结合着代码来测试一下,强引用就不用说了,软引用的描述也很清楚,关键是 “弱引用” 与 “虚引用”。
这里写图片描述

由于System.gc()是告诉JVM这是一个执行GC的好时机,但具体执不执行由JVM决定,因此当JVM决定执行GC,得到的结果便是(事实上这段代码一般都会执行GC):
  java.lang.Object@de6ced
  null
  null
  java.lang.ref.WeakReference@1fb8ee3

从执行结果得知,通过调用weakRef.get()我们得到了obj对象,由于没有执行GC,因此refQueue.poll()返回的null,当我们把obj = null;此时没有引用指向堆中的obj对象了,这里JVM执行了一次GC,我们通过weakRef.get()发现返回了null,而refQueue.poll()返回了WeakReference对象,因此JVM在对obj进行了回收之后,才将weakRef插入到refQueue队列中。

这里写图片描述

同样,当JVM执行了GC,得到的结果便是:
  null //phanRef总是返回null
  null //refQueue在进行了gc后,才会有gc过程中遇到的phanRef对象
  null //phanRef总是返回null
  java.lang.ref.PhantomReference@1fb8ee3

从执行结果得知,我们先前说的没有错,phanRef.get()不管在什么情况下,都会返回null,而当JVM执行GC发现虚引用之后,JVM并没有回收obj,而是将PhantomReference对象插入到对应的虚引用队列refQueue中,当调用refQueue.poll()返回PhantomReference对象时,poll方法会先把PhantomReference的持有队列queue(ReferenceQueue< ? super T >)置为NULL,NULL对象继承自ReferenceQueue,将enqueue(Reference paramReference)方法覆盖为return false,而此时obj再次被GC发现时,JVM再将PhantomReference插入到NULL队列中便会插入失败返回false,此时GC便会回收obj。事实上通过这段代码我们也的却看不出来obj是否被回收,但通过 PhantomReference 的javadoc注释中有一句是这样写的:

Once the garbage collector decides that an object obj is phantom-reachable, it is being enqueued on the corresponding queue, but its referent is not cleared. That is, the reference queue of the phantom reference must explicitly be processed by some application code.一旦GC决定一个“obj”是虚可达的,它(指PhantomReference)将会被入队到对应的队列,但是它的指代并没有被清除。也就是说,虚引用的引用队列一定要明确地被一些应用程序代码所处理。
当GC一但发现了虚引用对象,将会将PhantomReference对象插入ReferenceQueue队列,而此时PhantomReference所指向的对象并没有被GC回收,而是要等到ReferenceQueue被你真正的处理后才会被回收。
虚引用的用法:
Phantom references are useful for implementing cleanup operations that are necessary before an object gets garbage-collected. They are sometimes more flexible than the finalize() method.
虚引用在实现一个对象被回收之前必须做清理操作是很有用的。有时候,他们比finalize()方法更灵活。

很明显的,虚引用可以用来做对象被回收之前的清理工作。

区别:

SoftReference比WeakReference生命力更强,当JVM的内存不吃紧时,即使引用的对象被置为空了,Soft还可以保留对该对象的引用,此时的JVM内存池实际上还保有原来对象,只有当内存吃紧的情况下JVM才会清除Soft的引用对象,并且会在未来重新加载该引用的对象。

而WeakReference则当清理内存池时会自动清理掉引用的对象。
WeakReference与SoftReference都可以用来保存对象的实例引用,这两个类与垃圾回收有关。

WeakReference是弱引用,其中保存的对象实例可以被GC回收掉。这个类通常用于在某处保存对象引用,而又不干扰该对象被GC回收,通常用于Debug、内存监视工具等程序中。因为这类程序一般要求即要观察到对象,又不能影响该对象正常的GC过程。

SoftReference是强引用,它保存的对象实例,除非JVM即将OutOfMemory,否则不会被GC回收。这个特性使得它特别适合设计对象Cache。对于Cache,我们希望被缓存的对象最好始终常驻内存,但是如果JVM内存吃紧,为了不发生OutOfMemoryError导致系统崩溃,必要的时候也允许JVM回收Cache的内存,待后续合适的时机再把数据重新Load到Cache中。这样可以系统设计得更具弹性。

根据GC 的工作原理,我们可以通过一些技巧和方式,让GC 运行更加有效率,更加符合应用程序的要求。一些关于程序设计的几点建议:

  1.最基本的建议就是尽早释放无用对象的引用。大多数程序员在使用临时变量的时候,都是让引用变量在退出活动域(scope)后,自动设置为 null.我们在使用这种方式时候,必须特别注意一些复杂的对象图,例如数组,队列,树,图等,这些对象之间有相互引用关系较为复杂。对于这类对象,GC 回收它们一般效率较低。如果程序允许,尽早将不用的引用对象赋为null.这样可以加速GC 的工作。

  2.尽量少用finalize函数。finalize函数是Java 提供给程序 员一个释放对象或资源的机会。但是,它会加大GC 的工作量,因此尽量少采用finalize 方式回收资源。

  3.如果需要使用经常使用的图片,可以使用soft应用类型。它可以尽可能将图片保存在内存中,供程序调用,而不引起 OutOfMemory.

  4.注意集合数据类型,包括数组,树,图,链表等数据结构,这些数据结构对GC 来 说,回收更为复杂。另外,注意一些全局的变量,以及一些静态变量。这些变量往往容易引起悬挂对象(dangling reference),造成内存浪费。
  5.当程序有一定的等待时间,程序员可以手动执行System.gc (),通知GC 运行,但是Java 语言规范并不保证GC 一定会执行。使用增量式GC 可以缩短Java 程序的暂停时间。

<think>好的,我现在需要回答用户关于Java四种引用及其与垃圾回收(GC)关系的问题。首先,我要确保自己正确理解这四种引用的类型它们各自的特点。Java的四种引用包括强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference虚引用(Phantom Reference)。这些引用类型在GC时的处理方式不同,影响对象的生命周期。 接下来,我需要回忆每种引用的定义使用场景。强引用是最常见的,只要对象有强引用存在,GC就不会回收它。软引用用于描述一些还有用但非必需的对象,在内存不足时会被回收。弱引用比软引用更弱,只能存活到下一次GC前。虚引用主要用于跟踪对象被回收的状态,必须与引用队列(ReferenceQueue)配合使用。 然后,我需要考虑如何组织这些信息,使其结构清晰。可能按照引用类型分点说明,每种引用解释其定义、特点、在GC中的表现以及典型应用场景。同时,要说明它们如何影响GC的行为,例如软引用如何帮助避免内存溢出,弱引用在缓存中的应用,虚引用如何用于资源清理等。 另外,用户可能还希望了解这些引用在实际开发中的使用例子,或者为什么需要不同的引用类型。比如,为什么使用弱引用而不是软引用?这时候需要解释它们的不同回收策略适用场景。 需要注意术语的准确性,例如正确区分ReferenceQueue在虚引用中的作用,以及不同引用类型在内存紧张时的回收顺序。还需要提到引用队列(ReferenceQueue)的作用,当被引用的对象被回收后,引用本身会被加入队列,允许程序进行后续处理。 可能还需要提到Java的垃圾回收机制如何与这些引用交互,例如在可达性分析中,不同的引用类型如何影响对象的可达状态。例如,软引用、弱引用虚引用的存在不会阻止对象被回收,只是在不同条件下触发回收。 最后,总结这四种引用如何帮助开发者更灵活地管理内存,优化应用性能,避免内存泄漏或内存溢出问题。同时,强调正确使用这些引用类型的重要性,避免误用导致的问题。 在整理过程中,要确保语言通俗易懂,结构层次分明,可能采用分点、分段的方式,每个引用类型单独一部分,每部分包含定义、特点、GC关系示例。此外,使用代码示例或实际应用场景来帮助理解,但根据用户的问题,可能不需要详细代码,而是更注重概念解释。 还需要检查是否有遗漏的信息,比如是否提到引用队列的具体作用,或者是否有其他相关的知识点需要补充,例如FinalizerPhantomReference的区别,或者Java 8之后是否有相关变化等。确保回答全面准确,不出现错误的信息。 总之,回答需要涵盖四种引用的定义、各自特点、在GC中的处理方式、使用场景以及它们如何帮助内存管理。同时保持结构清晰,语言简洁,便于用户理解。</think>--- ### Java 四种引用类型及其与 GC 的关系 Java 的引用类型决定了对象的生命周期如何被垃圾回收器(GC)管理。通过不同强度的引用,开发者可以更灵活地控制内存,优化应用性能。以下是四种引用的核心逻辑及与 GC交互: --- #### 一、**强引用(Strong Reference)** - **定义**:最常见的引用类型,通过 `new` 关键字直接创建。 ```java Object obj = new Object(); // 强引用 ``` - **GC 关系**: - 只要强引用存在,对象**绝不会被回收**。 - 即使内存不足(OOM),GC 也不会回收强引用对象,直接抛出 `OutOfMemoryError`。 - **应用场景**:普通对象创建,需长期存活的业务核心对象。 --- #### 二、**软引用(Soft Reference)** - **定义**:通过 `SoftReference` 类实现,描述“有用但非必需”的对象。 ```java SoftReference<Object> softRef = new SoftReference<>(new Object()); ``` - **GC 关系**: - **内存充足时**:对象保留,不会被回收。 - **内存不足时**:GC 会回收软引用对象(优先回收长时间未使用的对象)。 - 回收策略:在抛出 `OutOfMemoryError` **前**,尽可能多地释放软引用对象。 - **应用场景**:缓存(如图片缓存),避免频繁重建但允许内存紧张时释放。 --- #### 三、**弱引用(Weak Reference)** - **定义**:通过 `WeakReference` 类实现,强度低于软引用。 ```java WeakReference<Object> weakRef = new WeakReference<>(new Object()); ``` - **GC 关系**: - **无论内存是否充足**:只要发生 GC,弱引用对象**必定被回收**。 - 回收条件:仅当对象**无强引用或软引用**时生效。 - **应用场景**: - 临时缓存(如 `WeakHashMap` 的键)。 - 监听器注册,避免因未注销导致内存泄漏。 --- #### 四、**虚引用(Phantom Reference)** - **定义**:通过 `PhantomReference` 类实现,必须关联**引用队列(ReferenceQueue)**。 ```java ReferenceQueue<Object> queue = new ReferenceQueue<>(); PhantomReference<Object> phantomRef = new PhantomReference<>(new Object(), queue); ``` - **GC 关系**: - **对象回收前**:虚引用会进入关联的引用队列。 - **无法通过虚引用访问对象**:`get()` 方法始终返回 `null`。 - 唯一作用:**监听对象被回收的时机**。 - **应用场景**: - 精准控制资源释放(如堆外内存管理)。 - 替代 `finalize()` 方法(更可靠且无性能问题)。 --- ### 四类引用与 GC 的对比总结 | 引用类型 | 实现类 | 回收条件 | 典型应用场景 | 是否关联引用队列 | |------------|-------------------|---------------------------|----------------------------|------------------| | **强引用** | 直接赋值(默认) | 永不回收 | 核心业务对象 | 否 | | **软引用** | `SoftReference` | 内存不足时回收 | 缓存 | 可选 | | **弱引用** | `WeakReference` | 每次 GC 均可能回收 | 监听器、临时缓存 | 可选 | | **虚引用** | `PhantomReference`| 对象回收前入队,需手动处理 | 资源清理、替代 `finalize()` | 必须 | --- ### 关键机制:**引用队列(ReferenceQueue)** - **作用**:当引用指向的对象被回收时,引用对象本身会进入队列。 - **使用流程**: 1. 创建引用时绑定队列: ```java ReferenceQueue<Object> queue = new ReferenceQueue<>(); WeakReference<Object> ref = new WeakReference<>(new Object(), queue); ``` 2. 轮询队列获取被回收的引用: ```java Reference<?> clearedRef = queue.poll(); if (clearedRef != null) { // 执行后续清理操作 } ``` - **典型应用**: - 虚引用监听对象回收事件。 - 弱引用自动清理缓存条目(如 `WeakHashMap` 内部实现)。 --- ### 引用与 GC 的协作机制 1. **可达性分析阶段**: - GC 会遍历所有引用链,标记存活对象。 - 软、弱、虚引用不影响对象的可达性(仅强引用能阻止回收)。 2. **回收优先级**: - 强引用 > 软引用 > 弱引用 > 虚引用。 - 例如:若对象同时被强引用弱引用指向,GC 不会回收它。 3. **Finalization 的替代方案**: - `finalize()` 方法存在不确定性,虚引用+引用队列的组合更可靠。 --- ### 实际开发中的注意事项 1. **避免滥用软引用**:频繁的内存压力可能导致软引用缓存失效,反而降低性能。 2. **弱引用与内存泄漏**: - 若弱引用对象被意外保留(如放入 `static` 集合),仍会导致泄漏。 3. **虚引用的资源管理**: - 需配合 `try-finally` 或 `Cleaner` 类确保资源释放。 - 示例(Java 9+ 的 `Cleaner`): ```java Cleaner cleaner = Cleaner.create(); cleaner.register(obj, () -> releaseExternalResource()); ``` --- ### 总结 Java 的四种引用提供了不同强度的对象生命周期控制: - **强引用**保障核心对象存活,**软引用**优化缓存,**弱引用**避免泄漏,**虚引用**实现精准回收监听。 - 合理选择引用类型,可以在内存安全与性能之间取得平衡,尤其在高并发或资源敏感场景中至关重要。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值