1.如何判断对象是否存活
1.1 引用计数算法
概念:在对象头部增加一个引用计数器,每当有一个地方引用它时,计数器值就加一;当引用失效时,计数器值就减一;任何时刻计数器为零的对象就是不可能再被使用的。
优点:速度快,高效
缺点: 假如此时A引用B,B引用A那么,计数永远不为0,那么永远无法回收他们
1.2 可达性分析算法:
概念:通过一系列称为“GC Roots”的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为“引用链”(Reference Chain),如果某个对象到GC Roots间没有任何引用链相连,或者用图论的话来说就是从GC Roots到这个对象不可达时,则证明此对象是不可能再被使用的。
总结概念: 也就是找到是否连接根节点,此时如果A引用B,B引用A就不会发生,循环回收不掉的问题,因为他们找不到连接的根节点,也是当前主流的判断对象是否存活的算法
注意:即使在可达性分析算法中判定为不可达的对象,也不是“非死不可”的,这时候它们暂时还处于“缓刑”阶段,要真正宣告一个对象死亡,至少要经历两次标记过程:如果对象在进行可达性分析后发现没有与GC Roots相连接的引用链,那它将会被第一次标记,随后进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。
2.再谈引用
无论是通过引用计数算法判断对象的引用数量,还是通过可达性分析算法判断对象是否引用链可 达,判定对象是否存活都和“ 引用 ” 离不开关系。在JDK 1.2 版之前, Java 里面的引用是很传统的定义: 如果reference 类型的数据中存储的数值代表的是另外一块内存的起始地址,就称该 reference 数据是代表 某块内存、某个对象的引用。这种定义并没有什么不对,只是现在看来有些过于狭隘了,一个对象在 这种定义下只有“ 被引用 ” 或者 “ 未被引用 ” 两种状态,对于描述一些 “ 食之无味,弃之可惜 ” 的对象就显 得无能为力。譬如我们希望能描述一类对象:当内存空间还足够时,能保留在内存之中,如果内存空间在进行垃圾收集后仍然非常紧张,那就可以抛弃这些对象 —— 很多系统的缓存功能都符合这样的应用场景。所以在JDK 1.2版之后,Java对引用的概念进行了扩充,引入了四种引用
3.四种引用
2.1 强引用
是最传统的“引用”的定义,是指在程序代码之中普遍存在的引用赋值,即类似“Object obj=new Object()”这种引用关系。无论任何情况下,只要强引用关系还存在,垃圾收集器就永远不会回收掉被引用的对象。
2.2 软引用
是用来描述一些还有用,但非必须的对象。只被软引用关联着的对象,在系统将要发生内存溢出异常前,会把这些对象列进回收范围之中进行第二次回收,如果这次回收还没有足够的内存,才会抛出内存溢出异常。在JDK 1.2版之后提供了SoftReference类来实现软引用。
2.3 弱引用
也是用来描述那些非必须对象,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生为止。当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。在JDK 1.2版之后提供了WeakReference类来实现弱引用。
2.4 虚引用
也称为“幽灵引用”或者“幻影引用”,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。在JDK 1.2版之后提供了PhantomReference类来实现虚引用。
3. 软引用的进一步了解
3.1 软引用的使用场景?
软引用主要用于实现内存敏感的高速缓存。当系统内存充足时,软引用的对象可以长时间驻留在内存中,供程序快速访问;当系统内存不足时,这些对象可以被垃圾回收器回收,从而避免内存溢出错误。这种机制非常适合用于缓存系统,如图片缓存、页面缓存等。
3.2 为什么用到了软引用?
内存压力下的灵活应对:软引用允许JVM在内存紧张时自动回收对象,从而避免了程序因为内存不足而崩溃的风险。
提高性能:通过保留软引用对象,在内存充足时可以直接访问这些对象,减少了重新加载或重新计算这些对象所需的时间和资源。
资源管理的灵活性:开发者可以通过软引用来控制缓存的大小和生命周期,而不是完全依赖于JVM的垃圾回收机制。
3.3 软引用的使用(小例子)
import java.lang.ref.SoftReference; public class SoftReferenceExample { public static void main(String[] args) { // 假设这是我们需要缓存的数据 Object data = new Object(); // 创建一个软引用指向这个数据 SoftReference<Object> softRef = new SoftReference<>(data); // 使用softRef.get()来获取软引用指向的对象 Object cachedData = softRef.get(); // 模拟内存紧张的情况(实际上在简单示例中无法直接模拟) // 在实际环境中,当JVM进行垃圾回收时,如果内存不足,可能会回收softRef指向的对象 // 再次尝试获取对象,如果对象已被回收,则返回null cachedData = softRef.get(); if (cachedData != null) { // 对象仍然存在,可以继续使用 } else { // 对象已被回收,需要重新加载或重新计算 } } }
注意:由于垃圾回收的时机和过程是由JVM的垃圾回收器控制的,因此我们不能精确地预测软引用对象何时会被回收。在实际应用中,开发者需要根据应用的内存需求和性能要求来合理使用软引用。
4. 弱引用的进一步了解
4.1 使用场景
- 缓存系统:
- 在一些缓存系统中,特别是当缓存的数据量很大且不是必需时,可以使用弱引用来引用这些数据。这样,当JVM内存不足时,这些数据可以被自动回收,从而避免内存溢出。
- 例如,
WeakHashMap
就是基于弱引用实现的,它允许键值对中的键是弱引用的。这意味着,如果某个键除了被WeakHashMap
所引用外,没有其他强引用指向它,那么这个键以及它所对应的值都可能被垃圾回收器回收。- 生命周期较短的临时对象:
- 对于那些生命周期较短,且对内存敏感的临时对象,可以使用弱引用来引用它们。这样,一旦这些对象不再被其他强引用所指向,它们就可以被垃圾回收器及时回收。
4.2 为什么用到了弱引用
- 内存管理的灵活性:
- 弱引用提供了一种比软引用更加灵活的内存管理方式。通过弱引用,开发者可以更加精确地控制对象的生命周期,避免内存泄漏,同时也能够确保在内存不足时能够自动回收非必需的对象。
- 减少内存泄漏的风险:
- 在一些复杂的应用程序中,由于对象之间的引用关系错综复杂,很容易出现内存泄漏的情况。通过使用弱引用,可以减少因为对象之间的强引用关系而导致的内存泄漏风险。
- 提升性能:
- 在一些性能敏感的应用程序中,及时回收不再使用的对象可以释放更多的内存空间,从而提升应用程序的性能。弱引用通过自动回收非必需的对象,有助于减少内存占用,提升性能。
4.3 弱引用的使用(小例子)
import java.lang.ref.WeakReference; public class WeakReferenceExample { public static void main(String[] args) { // 创建一个强引用对象 Object strongRef = new Object(); // 创建一个弱引用对象,指向强引用对象 WeakReference<Object> weakRef = new WeakReference<>(strongRef); // 验证弱引用是否有效 System.out.println(weakRef.get()); // 输出对象地址 // 将强引用置为null,模拟对象不再被强引用 strongRef = null; // 触发垃圾回收(注意:实际环境中,垃圾回收的时机是不确定的) System.gc(); // 再次验证弱引用是否有效 // 由于强引用已被置为null,且没有其他强引用指向该对象,因此该对象可能会被垃圾回收 // 此时,weakRef.get()可能返回null System.out.println(weakRef.get()); // 可能输出null } }
同样和弱引用相同:由于垃圾回收的时机和过程是由JVM的垃圾回收器控制的,因此我们不能精确地预测弱引用对象何时会被回收。在实际应用中,开发者需要根据应用的内存需求和性能要求来合理使用弱引用。