Android开发Android技术知识Android开发

五、JVM 系列(2) —— GC相关

2018-10-12  本文已影响4人  Yjnull

Java 与 C++ 之间有一堵由 内存动态分配和垃圾收集技术 所围成的 “高墙”,墙外面的人想进去,墙里面的人却想出来。

说起 垃圾收集(Garbage Collection, GC),我们需要思考 3件事情。

1. 哪些内存需要回收?

总结:Java 堆和方法区是垃圾收集器所关注的内存区域。

2. 什么时候回收?

在堆里面存放着 Java 世界中几乎所有的对象实例,所以哪些对象已经 “死去”(即不可能再被任何途径使用的对象)。就回收哪些。那么如何判断对象是否存活呢?

2.1 引用计数算法(Reference Counting)

2.2 可达性分析算法(Reachability Analysis)

基本思想:通过一系列的称为 “GC Roots” 的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为 引用链(Reference Chain),当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是不可用的。用一个图能很容易的理解。

可达性分析.png

从图中知道 GC Roots 的重要性,那么 Java 中,可作为 GC Roots 的对象包括下面几种:

2.3 Java 的四种引用,强软弱虚

2.3.1 强引用(Strong Reference)

强引用是指在代码中普遍存在的,如 Object obj = new Object() 这类的引用,只要强引用还在,GC 永远不会回收被引用的对象。

2.3.1 软引用(SoftReference)

用来描述一些 还有用但并非必需的对象,对于软引用关联的对象,在将要发生内存溢出之前,会将这些对象列进回收范围之中进行第二次回收。 如果这次回收后还是没有足够的内存,才会抛出内存溢出异常。

2.3.1 弱引用(WeakReference)

用来描述 非必需的对象。但是它的强度比 软引用 更弱一点。当 GC 工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。也就是说被 弱引用关联的对象 只能生存到下一次 垃圾收集 发生之前。

2.3.1 虚引用(PhantomReference)

也被称为 幽灵引用或者 幻影引用,最弱的一种引用关系。无法通过 虚引用 来取得一个对象实例。
为一个对象设置 虚引用 关联的唯一目的就是 能在这个对象被收集器回收时收到一个 系统通知。

2.4 回收方法区

方法区也被称为永久代,这里的垃圾收集主要回收两部分内容: 废弃常量无用的类

2.4.1 废弃常量

以常量池中 字面量 的回收为例,假如一个字符串 “abc” 已经进入了常量池中,但是当前系统没有任何一个 String 对象是叫做 “abc” 的,换句话说,就是没有任何 String 对象引用常量池中的 “abc” 常量。如果这时候发生内存回收,而且必要的话,这个 “abc” 常量就会被系统清理出常量池。常量池中的其他 类(接口)、方法、字段的符号引用也与此类似。

2.4.2 无用的类

判定一个类是否是 “无用的类” 的条件则苛刻许多。需同时满足下面3个条件才能算是 无用的类。

当一个类满足上述3个条件时,虚拟机 可以 对它进行回收。但也只是 “可以”,而并不是和对象一样,不使用了就一定会回收。也就是说虚拟机也可以不回收它。
是否对类进行回收,HotSpot 虚拟机提供了 -Xnoclassgc 参数进行控制,

3. 如何回收?

了解了 哪些内存区域需要回收、四种引用的区别 以及 什么时候会进行回收,那么现在要解决的就是 如何进行回收了。即:垃圾收集算法

3.1 标记-清除算法(Mark-Sweep)

概述:算法分为两个阶段 标记清除 两个阶段:首先标记出所有需要回收的对象,在标记完成后统一进行回收。

缺点:
1、效率问题:标记和清除两个过程的效率都不高;
2、空间问题:标记清除后悔产生大量不连续的内存碎片,空间碎片太多可能会导致以后需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次 GC。

3.2 标记-整理算法(Mark-Compact)

概述:与 标记-清除算法一样,但是后续步骤不是直接对 可回收对象 进行清理,而是 让所有存活的对象都向一端移动,然后直接清理掉 端边界之外的内存,这样就解决了 内存碎片的问题。

3.3 复制算法(Copying)

概述:将可用的内存按容量分为大小相等的两块,假设为 A块 和 B块,每次只使用其中的一块。当A块的内存用完了,就将还存活的对象复制到 B块 上面,然后再把 A块使用过的内存空间一次清理掉。
缺点:代价太高,将内存缩小为了原来的一半。

改进:现在的商业虚拟机都采用这种收集算法来回收 新生代。 而研究表明,新生代中的对象一般情况下98% 是 朝生夕死的,所以并不需要按照 1:1 的比例来划分内存空间。

3.4 分代收集算法(Generational Collection)

概述:当前商业虚拟机的 GC 都采用 “分代收集”算法。 这种算法没有什么新思想,只是根据对象存活周期的不同将内存划分为几块。
一般是将 Java 堆分为 新生代老年代,这样就可以根据每个年代的特点采用最适当的 收集算法。

参考

《深入理解 Java 虚拟机》

上一篇 下一篇

猜你喜欢

热点阅读