【Java 杂记】4. 内存回收

转载自https://blog.csdn.net/justloveyou_/article/details/71216049

内存回收需要解决三个问题:

  • 哪些内存需要回收? (对象是否可以被回收的两种经典算法: 引用计数法 和 可达性分析算法)

  • 什么时候回收? (堆的新生代、老年代、永久代的垃圾回收时机,MinorGC 和 FullGC)

  • 如何回收? (三种经典垃圾回收算法(标记清除算法、复制算法、标记整理算法)及分代收集算法 和 七种垃圾收集器)

如何确定一个对象是否可以被回收?

  1. 引用计数算法:判断对象的引用数量
    这种思路很容易理解,当一个对象的引用计数为0时,该对象就可以被回收。引用计数收集器可以很快的执行,并且交织在程序运行中,对程序需要不被长时间打断的实时环境比较有利,但其很难解决对象之间相互循环引用的问题:


    【Java 杂记】4. 内存回收_第1张图片
    对象之间相互引用
  2. 可达性分析算法:判断对象的引用链是否可达
    可达性分析算法的基本思想是把所有的引用关系看作一张有向图,实时监控对象是否可达,如果不可达,则就将其回收,这就可以消除循环引用的问题。这张图的起始点是一些列名为“GC Roots” 的对象,如果从这些起始点出发,无法到达某个对象,则该对象就可以被回收:


    【Java 杂记】4. 内存回收_第2张图片
    可达性分析算法

    在Java中,可作为 GC Root 的对象包括以下几种:

    • 虚拟机栈(栈帧中的局部变量表)中引用的对象
    • 方法区中类静态属性引用的对象
    • 方法区中常量引用的对象
    • 本地方法栈中Native方法引用的对象

如何回收?

标记清除算法

标记-清除算法分为标记和清除两个阶段。该算法首先从根集合进行扫描,对存活的对象对象标记,标记完毕后,再扫描整个空间中未被标记的对象并进行回收:


【Java 杂记】4. 内存回收_第3张图片
标记清除算法原理

标记清除算法的两个主要缺点是:

  • 标记和清除过程效率都不高;
  • 因为没有移动存活对象,所以会造成碎片问题。空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。

复制算法

复制算法将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。


【Java 杂记】4. 内存回收_第4张图片
复制算法原理

复制算法适用于对象存活率第的场景,现在实际的商用虚拟机都采用这种算法来回收新生代。因为研究发现,新生代中的对象每次回收都基本上只有10%左右的对象存活,所以需要复制的对象很少。

标记整理算法

标记整理算法与标记清除算法最显著的区别是:标记清除算法不进行对象的移动,并且仅对不存活的对象进行处理;而标记整理算法会将所有的存活对象移动到一端,并对不存活对象进行处理,因此其不会产生内存碎片。


【Java 杂记】4. 内存回收_第5张图片
标记整理算法原理

分代收集算法

分代收集算法是基于这样一个事实:不同的对象的生命周期(存活情况)是不一样的,而不同生命周期的对象位于堆中不同的区域,因此对堆内存不同区域采用不同的策略进行回收可以提高 JVM 的执行效率。当代商用虚拟机使用的都是分代收集算法:新生代对象存活率低,就采用复制算法;老年代存活率高,就用标记清除算法或者标记整理算法。

【Java 杂记】4. 内存回收_第6张图片
垃圾回收算法总结

垃圾收集器

如果说垃圾收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。下图展示了7种作用于不同分代的收集器,其中用于回收新生代的收集器包括Serial、PraNew、Parallel Scavenge,回收老年代的收集器包括Serial Old、Parallel Old、CMS,还有用于回收整个Java堆的G1收集器。不同收集器之间的连线表示它们可以搭配使用。

【Java 杂记】4. 内存回收_第7张图片
垃圾收集器

你可能感兴趣的:(【Java 杂记】4. 内存回收)