垃圾收集器
引用计数器:
从gc日志可以看出是回收了,java虚假没有选用引用计数器算法管理内存
可达性分析算法
?线程池中线程是维持一个数量还是,用已经有的线程?
回收方法区
垃圾收集器
垃圾收集器根据应用场景和内存回收范围来选择。根据新生代、老年代,高性能服务器、客户端,计算密集场景、高响应场景。选择不同的收集器。
单线程收集器
单线程收集器,是使用单线程去完成垃圾回收工作。在垃圾收集的同时要暂停其它所有的工作线程,直到收集结束。即"Stop The World"。
多线程收集器
并行收集器:多条垃圾回收线程。工作线程处于等待状态。
并发收集器:多线程垃圾回收。工作线程继续运行。
HotSpot垃圾回收器
1、Serial(串行收集器):用于新生代GC,复制算法,启动时默认为Client模式,GC的线程为单线程。单线程性能很高。对于内存较小的新生代,停顿时间很短。
2、ParNew(并行收集器):与Serial类似,不同点在于它一般用于新生代的并行回收,而且通常情况下运行在Server模式。只有他能与CMS收集器配合工作。
3、Parallel Scavenge(并行回收):设计目标达到一个可控制的吞吐量。用于新生代回收,复制算法,并行收集。适用于后台预算不需要太多交互的任务。通过参数可控制吞吐量和停顿时间。自适应调节策略打开之后会根据虚拟机监控信息动态调整新生代。
4、Serial Old(串行收集器):是Serial收集器的老年代版本,使用标记-整理收集算法,主要用于启动模式为Client的JVM或者用于CMS收集器失败后的后备方案。
5、Parallel Old(并行收集器):是Parallel Scavenge的老年代版本,使用标记-整理收集算法。设计目标为吞吐量优先。搭配Parallel Scavenge,用于高吞吐量以及CPU敏感场合。目前JDK1.8作为默认的垃圾收集器。
6、Concurrent Mark Sweep,CMS(并发收集器):目标为希望系统停顿时间最短。使用标记-清除算法,根据参数定期清理内存碎片。会产生大量的内存碎片,根据配置定期做碎片整理。如果因碎片问题导致垃圾回收失败,选择Serial 收集器做Full GC。
-
CMS Failure Mode
-
上文提到在正常的情况下CMS整个流程的暂停时间都是很短的,一般也就在10ms~100ms左右。然而这与线上的情况并不相符,线上集群在读写压力很大的情况下,经常会出现长时间的卡顿,有些卡顿甚至长达几分钟,导致很严重的读写阻塞,甚至会造成Region Server和Zookeeper之间Session超时,使得Region Server异常离线。实际上,CMS并不是很完美,它会在两种场景下产生严重的Full GC,接下来分别进行介绍。
-
Concurrent Failure
-
这种场景其实比较简单,假如现在系统正在执行CMS回收老生代空间,在回收的过程中新生代来了一批对象进来,不巧的是,老年代已经没有空间再容纳这些对象了。这种场景下,CMS回收器会停止继续工作,系统进入 'stop-the-world' 模式,并且回收算法会退化为单线程复制算法,重新分配整个堆内存的存活对象到S0中,释放所有其他空间。很显然,整个过程会非常'漫长'。但是这种问题也很容易解决,只需要让CMS回收器更早一点回收就可以避免。JVM提供了参数-XX:CMSInitiatingOccupancyFraction=N来设置CMS回收的时机,其中N表示当前老生代已使用内存占新生代总内存的比例,该值默认为68,可以将该值修改的更小使得回收更早进行。
-
Promotion Failure
-
假设此时设置XX:CMSInitiatingOccupancyFraction=60,但是在已使用内存还没有达到总内存60%的时候,已经没有空间容纳从新生代迁移的对象了。oh,my god!怎么会这样?罪魁祸首就是内存碎片,上文中提到CMS算法会产生大量碎片,当碎片容量积累到一定大小之后就会造成上面的场景。这种场景下,CMS回收器一样会停止工作,进入漫长的 'stop-the-world' 模式。JVM也提供了参数 -XX: UseCMSCompactAtFullCollection来减少碎片的产生,这个参数表示会在每次CMS回收垃圾之后执行一次碎片整理,很显然,这个参数会对性能有比较大的影响。
7、Garbage-First,G1(并发与并行)收集器:JDK1.7开始使用。目标为未来可以替换CMS收集器,使用标记-整理收集算法。可预测的停顿。并且可以同时用于新生代和老年代。思想:把内存分块,优先回收价值最大的块。关于G1收集器性能的测试。结果是略逊于CMS。
理解GC日志
//GC 表示年轻代垃圾回收 java虚拟机启动以来经过的秒数
[GC2017-02-21T14:52:06.941+0800: 343679.497:
//使用ParNew作为年轻代的垃圾回收:年轻代垃圾回收前的大小->回收后大小//(总大小),回收时间
[ParNew: 164680K->600K(184320K), 0.0022530 secs]
//堆垃圾回收前的大小->堆回收后大小(总大小),回收时间
426032K->261971K(1028096K), 0.0024010 secs]
//用户态、内核态消耗的cpu时间和实际耗时。
[Times: user=0.04 sys=0.00, real=0.00 secs]
Serial收集器中的新生代名为"Default NewGeneration",所以显示的是"[DefNew"。如果是ParNew收集器,新生代名称就会变为"[ParNew",意为"Parallel New Generation"。如果采用Parallel Scavenge收集器,那它配套的新生代称为"PSYoungGen",老年代和永久代同理,名称也是由收集器决定的。
垃圾回收时机
GC回收会等到每个线程都走到安全点时执行。
安全点:逆优化,保存方法链和java栈帧,用于停顿后的恢复。
安全区:安全点的扩展,有些情况比如sleep操作,但在这个区域内暂停都是安全的。
参考:https://blog.csdn.net/Michael__Jack/article/details/71249353