GC---垃圾回收
垃圾回收(GC)
JVM会对内存区域进行定期的检查,当运行到线程安全点的时候启动垃圾回收器,根据垃圾回收机制对内存区域进行内存回收。
-
为什么要有GC?
内存处理是编程人员最容易出现问题的地方,一旦稍有疏忽忘记回收内存就可能出现内存不足或系统卡顿、不稳定甚至崩溃的现象,造成严重的经济损失。所以可以交给可靠高效优秀的垃圾回收算法,编写垃圾回收器以到达垃圾自动回收的目的,Java就是这么做的,可以让JVM自动进行高效的内存管理,编程人员则只需要把重点放在业务逻辑的编写上,提高开发效率。
-
对象分代机制:
堆分为新生代和老年代,新生代又分为Eden、To survivor、from survivor,新生代存放刚创建的、存活时间不长的对象,老年代存放存活时间长,不会随便回收的对象。大部分对象(80%)创建后都是不久就销毁了,所以没必要放在老年区域。每个对象都会有一个生存年龄标志来标记这个对象存活了多久,一个对象创建后先放在新生代的Eden区,经过一轮minorGC后,存活下来,年龄+1。当到达一定年龄,对象就会进入To survivor区,From survivor区也有一些对象,此时当minorGC运行下来,to中对象就会进入from区,而原先from区中的对象若仍然存活,年龄+1,并正式进入老年区,老年区不再轻易回收,这轮GC下来,to区就空了,from是幸存者区,那么此时的from就变成了to,to就变成了from,即下一轮GC中他们就角色互换了,并按照to和from的特性进行操作。
老年区的对象则使用majorGC(或fullGC)进行回收,minorGC是不会涉及到老年区的,而fullGC既会清理老年区又会清理新生区。
-
垃圾回收机制
对象是否回收是根据他是否还被引用来决定的,就需要对对象进行标记。有了标记之后就可以确定哪些对象可以进行清除回收。
-
对象标记
- 计数算法:简单高效,对象引用了一次,他的引用计数器就+1,否则-1,若计数器为0则代表无人引用,可以回收了,缺点是需要额外储存和计数开销,致命缺点是无法处理循环引用问题,故java中无垃圾回收器用此算法(python支持)。
- 可达性分析(根搜索算法、追踪算法,java、C#的选择):可以有效解决计数算法中循环引用问题。首先 根集合(GCRoot) 作为根对象集合,并从根出发寻找引用链路中是否直接或间接引用了某个对象,那么一轮寻找下来,就可以知道哪些对象不可达,即没有引用,可被回收。
-
对象清除
GC暂停所有线程,即stop the world!然后线性遍历整个堆,进行清除回收
-
标记清除算法(Mark-Sweep)
简单,不算高效,产生内存碎片。 若堆内存规整: 用指针碰撞(指针在空闲一端移动对象大小相等的距离即可);不规整(一般都不规整):并不是真的清空物理地址空间,而是维护一个空闲列表,存放被空闲的内存地址,下次需要分配内存就判断空闲地址空间够不够,够就直接覆盖。
-
复制算法(Copying)
简单高效!不会产生内存碎片,但需要2倍空间。若对象可达,复制到新空间中去,对象不可达就不复制,那么原来的老空间就全部空闲了。
-
标记压缩(标记整理)算法(Mark-Compact)
效率最低,实现较为复杂,但只需要持有一个有效起始地址即可 而不需要维护一个空闲列表。需要一个分界线,将可达对象移动到分界线一端,不可达对象放在另一端。
-
java中采用终极方案:分代收集算法
进行对象分代,根据各代的特点采用不同算法回收。前三种算法优劣都是要视具体情况而定,比如from to Survivor区因为较小就可以用复制算法,但老年区较大就不适合用复制算法了,而是用标记整理算法。
-
-
JAVA垃圾回收器
并发垃圾回收,可能和用户线程一同执行,也可能是交替执行。
-
serial GC:古老简单,用复制算法收集新生代,单线程,STW,C1模式下默认选项。
-
serial Old:serial GC老年代版,工作在老年代,采用标记整理算法。
-
parNew:serial GC多线程版本,配合老年代CMS GC工作。
-
CMS:基于标记清除算法,并发,老年代的垃圾回收器,占用CPU资源,宗旨是尽量减少停顿次数。无法处理浮动垃圾,可能出现"Concurrent Mode Failure"失败而导致一次full GC。CMS采用三次标记一次清除的办法,第一次初始标记,用来标记处gcroot可达的对象。第二次标记是并发标记,用户边跑边标记,因为是并发的,所以可能会产生浮动垃圾,本来可达,用户后面又改成不可达了。第三次标记是重新标记,用来标记第二次标记产生的变动对象。最后并发清除。三色标记法:黑色可达,灰色正在搜索,白色未搜索。
-
CMS默认的回收线程数是(CPU个数+3)/4。这个公式的意思是当CPU大于4个时,保证回收线程占用至少25%的CPU资源,这样用户线程占用75%的CPU,这是可以接受的。
-
concurrent mode failure是CMS垃圾收集器特有的错误,CMS的垃圾清理和引用线程是并行进行的,如果在并行清理的过程中老年代的空间不足以容纳应用产生的垃圾,则会抛出concurrent mode failure
-
CMS有什么问题?若CMS在清理过程已经出现内存不足,CMS就会失败。
-
CMS怎么解决内存碎片问题?启动老年代serialOld串行化收集(STW),导致程序长时间中断。CMS 提供了CMSInitiatingOccupancyFraction参数来设置老年代空间使用百分比,达到百分比就进行垃圾回收,默认92%。
-
-
parallel Scavenge:复制算法并行收集。java8默认GC。C2模式下默认选择,吞吐量优先,新生代老年代GC都是并行的。
-
G1:兼顾吞吐量和停顿时间,JDK9后的默认GC。在堆区分带的基础上引入分区概念,将堆分为若干Region,可以并行收集这些Region。G1拥有yound GC和mixGC(G1特有),和fullGC不一样,mixGC只收集老年区。
-
-