JVM学习笔记二:GC 垃圾回收

概述

学过一点java的都知道,java的GC是有JVM自动执行的。java内存运行时区域的各个部分,其中程序计数器、栈(虚拟机栈、本地方法栈)这些区域随着线程而生随线程灭而亡,这些区域内存分配和回收都有其确定性。Java堆和方法区则不同,这部分内存的分配和回收都是动态的,垃圾回收所关注的就是这部分内存区域。

如何判断对象已“死”

垃圾收集器在对堆内存进行回收之前,一定要确定内存中的对象中那些存活,那些死亡,存活的对象得以保留,死亡的对象将会被清除,将所占有的内存空间释放出来。一般,判断对象存活方式有两种:

  1. 引用计数法
  2. 可达性分析算法

引用计数法

引用计数法的判断方式很简单:给对象添加一个引用计数器,每当有一个地方引用它时,计数器加1,当引用失效时,计数器就减一,任何时候计数器值为0,则对象不可被使用,GC就予以清除。

这种方法的优点就在于简单,效率很高。但它无法解决对象之间循环引用的问题。

JVM学习笔记二:GC 垃圾回收_第1张图片

如图,对象A和对象B相互引用,除此之外,无其他引用,实际上这两个对象已经不能在被访问,但此时计数器数为1 ,于是引用计数器算法无法通知GC回收他们。

可达性分析

java使用的就是可达性分析算法判断对象是否存活,基本思想是:通过一些列称为“GC Root”的对象作为起点,从这些接点向下搜索,搜索的路径称为引用链,当一个对象到GC root没有任何引用链相连时,则证明对象不可用。

JVM学习笔记二:GC 垃圾回收_第2张图片

java中可以作为GC roots的对象包括:
1. 虚拟机栈中引用的对象(本地变量表)
2. 方法区中类静态属性引用的对象
3. 方法区中常量引用的对象
4. 本地方法栈中引用的对象(Native对象)

补充

在可达性分析算法中当对象不可达时,GC并不是立即执行清理掉的。清理掉一个对象时,至少要经历两次标记的过程。
1. 当对象不可达时,对象将被第一次标记并进行一次筛选:判断对象是否有必要执行finalize(),当对象没有覆盖finalize()方法,或者已经被虚拟机调用过,则被视为没有必要执行。
2. 当对象被判定有必要执行finalize()方法后,会将对象放置入一个F-Queue队列中,并在稍后由一个低优先记得finalize线程去执行它。(并不保证等待他运行结束)稍后GC会对F-Queue中对象进行第二次小规模标记,如果没有逃脱,则被回收。

垃圾回收算法思想

垃圾回收算法有很多种,当前主流商业虚拟机的垃圾收集都是根据对象存活周期的不同将内存划分为几块。一般将java堆分为新生代和乐生代,然后根据内存区域的特点采用“分代收集“。新生代中每次垃圾收集时都发现有大批对象死去,只有少量存活,就采用复制算法。而老生代因为对象存活率高,没有额外的空间担保,就必须使用标记-清理或者标记-整理来回收

复制算法

将可用内存按容量分为Eden区和两块survivor区,每次使用Eden区和一块survior区。当回收时将存活的对象一次性的复制到另一块survior区中,然后清理掉Eden和survior空间。Hotspot默认Eden和survior的大小比例是8:1。当survior空间不够用时,需要老生代进行分配担保。

标记-整理算法

老生代一般采用此算法。首先标记所有需要回收的对象,然后将所有存活的回想都想一端移动,然后直接清理掉端边界以外的内存。

内存分配和回收策略

对象优先在Eden分配

大多数情况下,对象在新生代Eden区中分配。当Eden没有空间进行分配时,虚拟机将发起一次Minor GC

大对象直接进入老年代

所谓大对象指,需要大量连续内存空间的java对象,最典型的大对象就是那种很长的字符串以及数组。

长期存活的对象将进入老年代

虚拟机给每个对象定义了一个对象年龄计数器,对象在survior区域中每度过一次minorGC 年龄就增加一岁,当年龄增加到一定程度(默认15),就晋升老年代。

动态对象年龄判定

为了更好的适应不同的内存情况,jvm并不是永远要求对象年龄必须达到MaxTenuringThreshold才能晋升老年代。
如果在survior空间中相同年龄所有对象大小的总和大于Survior空间的一半,年龄大于或等于改年龄的对象就可以直接进入老年代

空间分配担保

在MinorGC之前,虚拟机会先检查老年代最大可用连续空间是否大于新生代所有对象总空间,如果条件成立,则MInorGC可以确保是安全的。如果不成立,虚拟机会查看HandlePromotionFailure 设置值是否允许担保失败。
如果允许,则检查老年代最大可用连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,则尝试进行一次MinorGC。
如果小于,或不允许冒险,则进行一次FullGC。

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