1.标记清除算法
概念
该算法有两个阶段。
1. 标记阶段:找到所有可访问的对象,做个标记
2. 清除阶段:遍历堆,把未被标记的对象回收
应用场景
该算法一般应用于老年代,因为老年代的对象生命周期比较长。
优缺点
标记清除算法的优点和缺点
1. 优点
- 是可以解决循环引用的问题
- 必要时才回收(内存不足时)
2. 缺点:
- 回收时,应用需要挂起,也就是stop the world。
- 标记和清除的效率不高,尤其是要扫描的对象比较多的时候
- 会造成内存碎片(会导致明明有内存空间,但是由于不连续,申请稍微大一些的对象无法做到),
2.标记整理算法
标记清除算法和标记整理算法非常相同,但是标记压整理法在标记清除算法之上解决内存碎片化。 把可达和不可达的做了排序。
整理算法简单介绍
任意顺序 : 即不考虑原先对象的排列顺序,也不考虑对象之间的引用关系,随意移动对象;
线性顺序 : 考虑对象的引用关系,例如a对象引用了b对象,则尽可能将a和b移动到一块;
滑动顺序 : 按照对象原来在堆中的顺序滑动到堆的一端。
优缺点
优点:解决内存碎片问题,缺点整理阶段,由于移动了可用对象,需要去更新引用。
3.复制算法
概念
如果jvm使用了coping算法,一开始就会将可用内存分为两块,from域和to域, 每次只是使用from域,to域则空闲着。当from域内存不够了,开始执行GC操作,这个时候,会把from域存活的对象拷贝到to域,然后直接把from域进行内存清理。
应用场景
coping算法一般是使用在新生代中,因为新生代中的对象一般都是朝生夕死的,存活对象的数量并不多,这样使用coping算法进行拷贝时效率比较高。jvm将Heap 内存划分为新生代与老年代,又将新生代划分为Eden(伊甸园) 与2块Survivor Space(幸存者区) ,然后在Eden –>Survivor Space 以及From Survivor Space 与To Survivor Space 之间实行Copying 算法。 不过jvm在应用coping算法时,并不是把内存按照1:1来划分的,这样太浪费内存空间了。一般的jvm都是8:1。也即是说,Eden区:From区:To区域的比例是 8:1:1
始终有90%的空间是可以用来创建对象的,而剩下的10%用来存放回收后存活的对象。
1、当Eden区满的时候,会触发第一次young gc,把还活着的对象拷贝到Survivor From区;当Eden区再次触发young gc的时候,会扫描Eden区和From区域,对两个区域进行垃圾回收,经过这次回收后还存活的对象,则直接复制到To区域,并将Eden和From区域清空。
2、当后续Eden又发生young gc的时候,会对Eden和To区域进行垃圾回收,存活的对象复制到From区域,并将Eden和To区域清空。
3、可见部分对象会在From和To区域中复制来复制去,如此交换15次(由JVM参数MaxTenuringThreshold决定,这个参数默认是15),最终如果还是存活,就存入到老年代
注意: 万一存活对象数量比较多,那么To域的内存可能不够存放,这个时候会借助老年代的空间。
优缺点
优点:在存活对象不多的情况下,性能高,能解决内存碎片和java垃圾回收算法之-标记清除 中导致的引用更新问题。
缺点: 会造成一部分的内存浪费。不过可以根据实际情况,将内存块大小比例适当调整;如果存活对象的数量比较大,coping的性能会变得很差。
4.分代收集算法
标记清除 复制 标记整理 整合到一起
概述
这种算法,根据对象的存活周期的不同将内存划分成几块,新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。可以用抓重点的思路来理解这个算法。
新生代对象朝生夕死,对象数量多,只要重点扫描这个区域,那么就可以大大提高垃圾收集的效率。另外老年代对象存储久,无需经常扫描老年代,避免扫描导致的开销。
对于新生代
在新生代,每次垃圾收集器都发现有大批对象死去,只有少量存活,采用复制算法,只需要付出少量存活对象的复制成本就可以完成收集;
对于老年代
而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须“标记-清除-压缩”算法进行回收。参看java垃圾回收算法之-标记_清除压缩
新创建的对象被分配在新生代,如果对象经过几次回收后仍然存活,那么就把这个对象划分到老年代。
老年代区存放Young区Survivor满后触发minor GC后仍然存活的对象,当Eden区满后会将存活的对象放入Survivor区域,如果Survivor区存不下这些对象,GC收集器就会将这些对象直接存放到Old区中,如果Survivor区中的对象足够老,也直接存放到Old区中。如果Old区满了,将会触发Full GC回收整个堆内存。
关于复制算法举例子分析:
so = s1 复制算法
这时候,垃圾收集机制发现只有User1 不可达,然后, User2和User3拷贝到S1区域,然后清除S0区域所有(User1被清除掉了)。此时S0啥都没有的,这时候是这样:
此时新创建的User4存放EDEN区域,如果发现经常被使用,晋升到S1区域。S0是没有任何数据的哦。为啥不放在S1而不是S0? 为了复制!
S0和S1一定要有块区域是空闲的,目的是为了存放下一次复制。此时:
此时User2和User3没有被继续使用,而User4继续使用。此时User4会复制到S0区,然后清空S1区。
这样做的优点就是: 解决碎片化问题,快速,清理干净。
缺点就是:浪费空间
复制算法一般用在新生代中~~ 上面画的就是新生代
标记清除算法和标记压缩算法很相似,但是标记压缩算法在标记清楚算法之上,解决内存碎片化。
标记压缩(也叫标记整理) 应用于老年代
红色是可达,黑色是不可达
堆内存:
进行移动整理:
不可以达的全部回收
新生代满了出发Minor GC
老年的满了出发 Major GC或者 Full GC
Full GC也会发生在永久区里面 如果满了 也会发生Full GC
Minor GC和Full GC区别
概念:
新生代 GC(Minor GC):指发生在新生代的垃圾收集动作,因为 Java 对象大多都具
备朝生夕灭的特性,所以 Minor GC 非常频繁,一般回收速度也比较快。
老年代 GC(Major GC / Full GC):指发生在老年代的 GC,出现了 Major GC,经常
会伴随至少一次的 Minor GC(但非绝对的,在 ParallelScavenge 收集器的收集策略里
就有直接进行 Major GC 的策略选择过程) 。MajorGC 的速度一般会比 Minor GC 慢 10
倍以上。
JVM的永久代中会发生垃圾回收么?
垃圾回收不会发生在永久代,如果永久代满了或者是超过了临界值,会触发完全垃圾回收(Full GC)。如果你仔细查看垃圾收集器的输出信息,就会发现永久代也是被回收的。这就是为什么正确的永久代大小对避免Full GC是非常重要的原因。请参考下Java8:从永久代到元数据区
(注:Java8中已经移除了永久代,新加了一个叫做元数据区的native内存区)