目录
1、垃圾收集器概述
1)、分代垃圾收集器
2)、GC算法
3)、选择GC算法
2、GC调优基础
1)、调整堆大小
2)、代空间的调整
3)、永久代和元空间的调整
4)、控制并发
5)、自适应调整
3、垃圾回收工具
五、垃圾收集器入门
page81-page102
主流的四个垃圾收集器:
Serial收集器(常用于单CPU环境)
throughput(或者parallel)收集器
concurrent收集器(CMS)
G1收集器
垃圾收集分为两步:查找不再使用的对象,然后释放这些对象所管理的内存。
jvm中的线程被分为应用线程和处理垃圾收集的线程;
在垃圾回收的过程中,对象的内存地址会发生变化,因此这个过程中任何应用线程都不应该再访问对象。
所有应用线程都停止运行所产生的停顿称为时空停顿(stop-the-world);
所有的GC算法都是将堆划分成老年代和新生代;
所有的GC算法在清理新生代对象时,都是使用了“时空停顿(stop-the-world)”方式的垃圾收集方法,通常这是一个能较快完成的操作;
分代设计的优势:
1、由于新生代只是堆的一部分,与处理整个堆相比,处理新生代的速度更快。即停顿的时间更短但是频率会更高。
2、源于新生代中对象的分配方式。新生代的minorGC,对象分配在Eden空间,垃圾收集时,新生代Eden空间被清空,原来Eden空间中的对象要么被移走,要么被回收;所有存活的对象要么被移动到另外一个survivor空间,要么被移动到老年代。相当于对新生代在垃圾回收时进行了一次压缩整理。
当老年代满的时候会触发FullGC
简单的做法是直接停掉所有的应用线程,找出不在使用的对象,对其进行回收,接着对堆空间进行整理,这通常导致应用程序线程长时间的停顿;另外还有可能在应用线程运行的同时找出不再使用的对象,如(CMS和G1收集器),他们不需要停止应用线程就能找到不在使用的对象,因此被称为低停顿收集器,其代价是消耗更多的CPU
最简单的一种,使用单线程清理堆,包括minorGC和FullGC,通过-XX:+UseSerualGC启用serial收集器,关闭的时候不能直接-XX:-UseSerualGC,需要指定另外一种收集器
是server型虚拟机默认的垃圾收集器。对minorGC和fullGC都是使用多线程的方式,也被称为parallel收集器;
在minorGC和fullGC时会暂停所有的应用线程,同时在fullgc过程中会对老年代空间进行压缩整理。
可以使用-XX:+UseParallelGC、-XX:+UseParallelOldGC标志启用收集器
特点:
能够在应用线程运行的同时并行地对老年代进行垃圾回收,该算法能避免应用程序发生fullGC,缺点消耗CPU
设计的初衷:
消除Serial收集器和throughput(或者parallel)收集器FullGC周期中的长时间停顿。
在minorGC时会暂停所有的应用线程,并以多线程的方式进行垃圾回收,和throughput不同的是,CMS使用的不是XX:+UseParallelGC算法而是XX:+UseParNewGC算法;
在fullGC的时候,不在暂停应用线程,而是使用若干个后台线程定期地对老年代进行扫描,及时回收其中不在使用的对象,这种算法使得CMS成为一个低延迟的收集器,
即:应用线程只在minorGC以及后台线程扫描老年代时发生极其短暂的停顿,和throughput收集器比起来短的多。
缺点:
消耗更高的CPU资源;
后台线程不在进行任何的压缩整理,会出现堆变得碎片化。
如何CMS无法获得足够的CPU资源或者堆过度碎片化,CMS就蜕化到serial收集器的行为:暂停所有的应用线程,使用单线程回收、整理老年代空间。这之后又恢复到并发运行、再次启动后台线程(直到下一次变得碎片化)。
通过-XX:+UseConcMarkSweepGC -XX:+UseParNewGC标志启用CMS垃圾回收器,默认情况下是关闭的。
特点:
也是能够在应用线程运行的同时并行地对老年代进行垃圾回收,在某种程度上能够减少发生fullGC的风险,G1的设计理念使得它比CMS更不容易遭遇fullGC,缺点消耗CPU
设计初衷:
为了尽量缩短处理超大堆(大于4G)时产生的停顿。
minorGC也是暂停所有的应用线程,使用多线程的方式完成;
设计的原理把堆内存分区(仍旧是分代),在fullGC时,不需要暂停应用线程,由于老年代被分到不同的区域,
G1收集器通过将对象从一个区域复制到另外的一个区域,完成对象的清理工作,这意味着在正常处理的过程中,G1收集器实现了堆的压缩整理(至少是部分整理)。
因此使用G1收集器不太容易发生碎片化---虽然这种问题无法避免。
通过标志-XX:+UseG1GC启用G1垃圾收集器,默认是关闭的。
缺点:
消耗CPU资源
system.gc()
一般情况下不建议使用手动触发GC
可以通过参数-XX:+DisableExplicitGC显式地禁止,默认情况下是关闭的。
不过在一些情况下是需要用到的,比如在做性能测试时,通常在获取堆转储之前强制进行一次fullGC。
serial收集器适用于内存使用少于100M的场景,这种场景其他三个收集器都发挥不了太大作用。
大多数是在throughput和concurrent(CMS和G1)收集器之间做选择。
throughput收集器和concurrent收集器如何选择?
CMS和G1如何选择?(都会存在并发模式失效的情况)
-Xms4096m 设置堆大小的初始值
-Xmx4096m 设置堆大小的最大值
如果堆的初始值小于最大值,在程序运行的过程中,jvm会自适应调整堆的大小,以使得GC的效率最好.但是如果自己确切知道自己需要多大的内存,将初始值和最大值设置为一样,这样省去jvm估算堆是否需要调整大小的消耗,GC的效率也会稍微提升.
为什么不把堆设置得越大越好?
1、GC停顿消耗的时间取决于堆的大小,这样虽然停顿的频率变少,但是他们持续的时间会让程序的整体性能变慢;
2、堆的大小受物理机的限制。操作系统使用虚拟内存机制管理物理内存,虽然一台机器的物理内存可能有8G,不过操作系统使得你会觉得有更多可用的内存。
当一旦设置的jvm堆大于物理内存,就会出现内存和磁盘之间进行数据的交换(swap),并且有可能使得fullGC时的并发模式失效。
总结:调整堆的大小的首要原则是不要将堆的大小设置得比物理机还大(包括一台机器上的多个jvm实例,其所有堆的和不能操作物理内存),此外,通常情况下还需要预留至少1G的内存空间。
所有用于调整代空间的命令行标志调整的都是新生代空间,新生代剩下的所有空间都是老年代占用。
-XX:NewRatio=N 设置新生代和老年代的空间占用比率(默认值是2,即占整个堆空间的1/3,计算公式:新生代大小=堆初始大小/(1+NewRatio))
-XX:NewSize=N 设置新生代空间的初始大小
-XX:MaxNewSize=N 设置新生代空间的最大大小
-XmnN 将newsize和maxsize设置为同一个值
优先级 NewSize的优先级高于NewRatio,这两种方式的设置,新生代的大小都会随着堆总体大小的变化而变化;
使用-XmnN 可以将新生代的大小固定在一个值,而不随堆总体的变化而变化
新生代过小或者过大的特点?
过小:频繁的进行minorGC
过大:垃圾收集器发生的频率比较低,从新生代晋升到老年代的对象也更少,但是这样的分配方式使得老年代相对比较小,比较容易被填满,会更频繁的触发fullGC
永久代(32位机器默认值64M,64位默认82M)
-XX:PermSize=N
-XX:MaxPermSize=N
元空间(默认没有限制)
-XX:MetaspaceSize=N
-XX:MaxMetaspaceSize=N
总的GC线程数目计算公:ParallelGCThreads = 8 + ((N-8)*5 / 8)
-XX:-UseAdaptiveSizePolicy关闭全局范围内自适应调整功能(默认是开启的)
jstat -gcutil process_id 1000