JVM学习笔记系列--垃圾回收算法

对象存活判断算法

垃圾收集器在进行对象回收之前,必须判断哪些对象是可回收的,这就引出了对象存活判定算法。

引用计数法

       主流的虚拟机都没有选用引用计数器算法来管理内存,最主要的原因就是它很难解决对象之间相互循环引用的问题。

可达性分析算法

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

可作为GC Roots的对象包括:

  • 虚拟机栈中引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中Native方法引用的对象

判断对象是否已死

即便可达性分析算法中不可达的对象,也并非“非死不可”。真正宣告一个对象死亡,至少要经历两次标记过程:

  1. 如果对象在进行可达性分析后发现没有与GC Roots相连接的引用链,那它将会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。若对象被判定为有必要执行finalize()方法,那么这个对象将会放置在一个叫做F-Queue的队列中,并在稍后有一个低优先级的线程去执行。
  2. GC将对F-Queue中的对象进行第二次小规模标记,若第二次标记时对象还没有在finalize()中重新与引用链中的任何一个对象建立关联,则此对象就会被回收。

回收方法区

方法区的垃圾收集主要回收两部分内容:废弃常量和无用的类。

判断一个常量是否是废弃的:

       假如一个字符串“abc”进入了常量池中,但系统中没有任何一个String对象引用常量池中的“abc”常量,也没有其他地方引用了这个字面量。

判断一个类是否无用需要同时满足:

  1. 该类所有的实例都已经被回收。
  2. 加载该类的ClassLoader已经被回收。
  3. 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

垃圾收集算法

标记-清除算法

算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,标记完成后统一回收所有被标记的对象。

其不足之处有两个:

  • 效率不高。
  • 空间问题,标记清除后会产生大量不连续的内存碎片,可能会导致后续程序运行中需要分配较大对象的时候无法找到足够大的连续内存而不得不提前触发另一次垃圾回收。

复制算法

       将可用内存划分为大小相等的两块,每次只使用其中的一块。当这一块用完了,就将还存活的对象复制到另一快上,然后再把已使用过的内存空间清理掉。这样做的好处是不需要考虑内存碎片的问题且运行高效;但代价就是可用内存缩小为了原来的一半。

       目前虚拟机都采用这种算法来回收新生代,一般是将新生代分为Eden、From Survivor和To Survivor,比例为8:1:1,每次使用Eden和其中的一块Survivor。当回收时,将Eden和Survivor中还存活的对象一次性的复制到另外一块Survivor空间上,最后清理掉Eden和刚才用过的Survivor空间。

标记-整理算法

       标记过程与标记-清除算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉边界以外的内存。

分代收集算法

       根据对象存活周期的不同将内存划分为几块,新生代这种每次垃圾收集时都有大量对象死去的,采用复制算法;老年代中对象存活率高,没有额外空间对它进行分配担保,就采用标记-清除或者标记-整理算法。

你可能感兴趣的:(JVM,学习笔记,jvm)