JVM
GC 的3种算法
1.Mark-Sweep(标记清除)

标记一块垃圾,然后直接清掉,优点:速度快。缺点:内存碎片化,零散内存太多,时间一长,就无法装下很大的对象了。
2.Copying(拷贝)

无论内存多大,全部一分为二,然后把需要的对象copy到另一半中,然后直接清掉原来一般的内存。有点:效率高。缺点:浪费空间。
3.Mark-Compact(标记压缩)

一边标记,一边整理,一边排列。优点:整理后的内存能更合理的分配资源。缺点:效率太低。
GC的知识体系

到目前位置,Java有两种对于内存的管理模型:

分代模型:

JVM堆分代:
1、JVM堆被分为了年轻代和老年代。年轻代的GC过程称为Yong GC,速度快较频繁。老年代的GC过程称为Full GC,速度较慢应该尽量避免。
2、对象被创建后,除了少部分大对象会在老年代分配内存外,大部分的对象首先都是在年轻代进行内存分配,而且大部分的对象都是“朝生夕死”,很快就会被年轻代的Yong GC回收掉。
3、老年代的内存空间一般会比年轻代的内存空间大,能存放的对象多,老年代的空间不足后会进行Full GC操作,比Yong GC耗时,所以应尽量避免频繁的Full GC操作。
新对象被分配到新生代,年龄为0,垃圾回收器每回收一次,年龄长一岁,当年龄到达一定值时间,该对象进入老年代。在新生代中GC采用的算法为copy算法,在老年代中,不同的GC采用不同的算法。
新生代区:
由于新生代区中的对象,大量消亡,少量存活的特点,所以采用复制算法,将年轻代中分为一个Eden区和两个Surviver区,比例为8:1:1,两个Surviver区分别称为From区和To区。对象在Eden区创建,经过一次Yong GC后,还存活的对象将会被复制到Surviver区的From区,此时To区是空的。到了下一次GC的时候,Eden区存活的对象会复制到Surviver区的To区,而Form区的对象有两个去处,From区的对象会根据经过的GC次数计算年龄,如果年龄到达了阈值(默认15,通过参数:-XX:MaxTenuringThreshold 配置),则会被移动到老年代中,否则就复制到To区,此时From区变成了空的,然后From区和To区进行角色互换,到下一次进行GC时,还是有一块空的To区,用来存放从eden区和From区移动过来的对象。
老年代区:
老年代区的内存一般会比新生代的内存要大,能存放的对象多,往往采用的是Mark-Compact或Mark-Compact或两种结合的算法进行GC处理。
分代分区的优点:
a、在年轻代新增Surviver区,有利于减轻老年代的负担,尽可能的让大部分对象在年轻代通过较高效的Yong GC回收掉,不至于老年代里存放的对象过多导致内存不足而进行频繁的Full GC操作。
b、这种分区有利于减少内存碎片的产生。
6种分代的垃圾回收器

1.Serial

stop-the-world简称STW:当垃圾回收器开始工作时,所有的业务线程全部停止。STW会引起卡顿,JVM调优的目标就是让STW的时间变短。
single GC:这种垃圾回收器是单线程进行垃圾回收处理。
2.Parallel Scavenge

multiple GC:多线程进行垃圾回收处理,也是采用 copying collector(拷贝算法)。JDK1.8默认为ParallelScavenge—ParallelOld分代模型(年轻代:ParallelScavenge,老年代:ParallelOld)简称 PS+PO。
3.CMS

从CMS往后,全都是并发处理:

GC不在进行STW,而是同业务线程并发的执行,就是边运行边清理。
浮动垃圾:由于GC与业务线程并发执行,而实际上线程并发执行也是有先后顺序的,很有可能,当GC标记一个对象不是垃圾的时候,业务线程断掉了这个对象的引用,导致这个对象变为垃圾,这时候GC并不会清理这个垃圾,因为在GC的线程中已经标记这个对象为有用对象,所以当前次的GC回收,就不会处理这个对象(而下次回收时会进行处理),这中对象垃圾被称为浮动垃圾。
GC进行标记的算法(三色标记算法):

所谓三色标记算法(黑色,灰色,白色),黑色:代表自己已经被标记,并且找到了下一级全部的节点,灰色:代表自己已经被标记,还没有进行它的下一级的节点的标记,白色:代表还没有遍历到的节点。

这种情况的产生,是因为,当GC线程标记了B为灰色——>业务线程断开了B与D的引用——>GC线程由于A是黑色的所以不会去寻找他的子节点,虽然B是灰色的但是由于B与D的引用断了,所以找不到D了,所以D被当成垃圾处理了——>业务线程建立起了A与D的引用(由于被清理,所以导致空指针的发生)
CMS的解决方案: Incremental Update

当A与D建立关系的时候,将A变为灰色,这样GC去判断A为灰色的时候,还会继续往下找到D。 而CMS这种标记会引发另外一个BUG:


首先当A中的1属性已经被标记完(假设指向了E对象),2还没开始标记,此时A被变为灰色——>业务线程将A的1属性与E断开,并于D建立引用关系——>GC线程继续标记完A中的2属性(假设指向了F对象),此时将A设为黑色,而此时GC标记的A.1指向E,A.2指向F,而D并没有被GC标记,所以D会被漏标。而E会被错误标记。
CMS为了解决此BUG,会对整个对象书进行重新标记:

而此时重新标记是会进行STW的,所以CMS的效率也不怎么高。






