JVM之Java垃圾回收机制
为大家推荐一篇写的通俗易懂的关于Java垃圾回收机制的文章
理解Java垃圾回收机制
Java主要把内存分为堆内存和非堆内存,堆内存存储的是对象实例和数组,而GC主要回收的内存就是堆内存。
堆内存模型
堆内存分为两大部分:新生代和老年代,比例为1:2。
老年代主要存放应用程序中生命周期长的存活对象。
新生代又分为三个部分:一个Eden区和两个Survivor区,比例为8:1:1。
Eden区存放新生的对象。
Survivor存放每次垃圾回收后存活的对象。
关注几个问题:
-
为什么要分新生代和老年代
答:Java的垃圾回收器采用的算法是分代回收算法。分代回收算法=复制算法+标记整理算法。不同类型的对象生命周期(新生代和老年代)决定了更适合采用哪种算法。 -
新生代为什么分一个Eden区和两个Survivor区?
假设新生代只有一个Eden区,当GC操作后,需要将Eden区存活对象复制到另外一块区,所以新生代需要额外划分一块Survivor区,用于存放GC后存活的对象。
为什么要有两个Survivor区?
因为第二次GC操作Eden区和Survivor区也需要被清理,这时就需要另一块空间,所以Survivor区需要一分为二。 -
一个Eden区和两个Survivor区的比例为什么是8:1:1?
答:新创建的对象都是放在Eden空间,这是很频繁的,尤其是大量的局部变量产生的临时对象,这些对象绝大部分都应该马上被回收,能存活下来被转移到survivor空间的往往不多。所以,设置较大的Eden空间和较小的Survivor空间是合理的,大大提高了内存的使用率。
8:1:1这个比例是可以调整的,包括上面的新生代和老年代的1:2的比例也是可以调整的。
可回收对象的判定算法
- 引用计数算法
给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。
- 可达性分析算法(根搜索算法)
从GC Roots作为起点,向下搜索它们引用的对象,可以生成一棵引用树,树的节点视为可达对象,反之视为不可达。
垃圾回收算法
- 标记清除算法 (Mark-Sweep)
标记-清除算法分为两个阶段:标记阶段和清除阶段。标记阶段的任务是标记出所有需要被回收的对象,清除阶段就是回收被标记的对象所占用的空间。
优点:简单,容易实现。
缺点:容易产生内存碎片
- 复制算法 (Copying)
复制算法将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用的内存空间一次清理掉。
优点:实现简单,不容易产生内存碎片
缺点:
- 对内存空间使用做出了高昂的代价
- 复制算法的效率跟存活对象数量有很大的关系,如果存活对象很多,那么复制算法的效率会大大降低。
- 标记整理算法 (Mark-Compact)
该算法标记阶段和Mark-Sweep一样,但是在完成标记之后,它不是直接清理可回收对象,而是将存活对象都向一端移动,然后清理掉端边界以外的内存。
优点:实现简单,不容易产生内存碎片
缺点:标记整理算法跟回收对象数量有很大的关系,如果回收对象很多,那么标记整理算法的效率大大降低。
可以看出复制算法和标记整理算法有点针对标记消除算法的缺点而诞生的。
- 分代回收算法
具体请看开头推荐的那篇文章
Minor GC与Major GC分别在什么时候发生?
Minor GC:通常是指对新生代的回收。指发生在新生代的垃圾收集动作,因为 Java 对象大多都具备朝生夕灭的特性,所以 Minor GC 非常频繁,一般回收速度也比较快
Major GC:通常是指对年老代的回收。
Major GC:Major GC除并发gc外均需对整个堆进行扫描和回收。指发生在老年代的 GC,出现了 Major GC,经常会伴随至少一次的 Minor GC。MajorGC 的速度一般会比 Minor GC 慢 10倍以上。