GC垃圾回收算法(接口)和垃圾回收器(实现类)的关系?分别是什么
GC算法(引用计数/ 复制/ 标清/ 标整)是内存回收的方法论,垃圾回收器就是算法落地实现;
因为目前为止还没有完美的收集器出现,更加没有万能的收集器,只是针对具体应用最合适的收集器,进行分代收集;
4种主要垃圾收集器:
① 串行垃圾回收器:
它为单线程环境设计且只使用一个线程进行垃圾回收,会暂停所有用户线程;所以不适合服务器环境;
② 并行垃圾收集器:是串行的加强版 ;他俩都会导致用户程序暂停 默认的垃圾收集器
多个垃圾收集器线程并行工作,此时用户线程是暂停的,适用于科学计算/ 大数据处理首台处理等弱交互场景;
③ 并发垃圾收集器:
先去别的地,打扫完了再换回来;有时候可以边执行用户程序边进行垃圾回收;有时候是你 停下来它再打扫; 暂停的效果比上边都好,并发标记清除会产生内存碎片;
用户线程和垃圾收集器线程同时执行(不一定是并行,可能交替执行),不需要停顿用户线程;互联网公司多用它,适用对响应时间有要求的场景。
④ G1
G1垃圾收集器将堆内存分割成不同的区域然后并发的对其进行垃圾回收
怎么查看服务器默认的垃圾收集器是哪个?生产上如何配置垃圾收集器的?谈谈你对垃圾收集器的理解?
java -XX:+PrintCommandLineFlags -version C:\Users\Administrator>java -XX:+PrintCommandLineFlags -version -XX:InitialHeapSize=266841920 -XX:MaxHeapSize=4269470720 -XX:+PrintCommandLineFl ags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesInd ividualAllocation -XX:+UseParallelGC //默认的垃圾收集器 java version "1.8.0_141" Java(TM) SE Runtime Environment (build 1.8.0_141-b15) Java HotSpot(TM) 64-Bit Server VM (build 25.141-b15, mixed mode)
默认的垃圾收集器有哪些?
java的gc回收的类型主要有几种:UseSerialGC,UseParallelGC,UseConcMarkSweepGC,UseParNewGC,UseParallelOldGC,UseG1GC
E:\20181111\javaSE\JUCJVM>jinfo -flag UseParallelGC 74940 默认的 -XX:+UseParallelGC 在IDEA中设置成: -XX:UseSerialGC E:\20181111\javaSE\JUCJVM>jinfo -flag UseSerialGC 72912 -XX:+UseSerialGC E:\20181111\javaSE\JUCJVM>jinfo -flag UseParallelGC 72912 -XX:-UseParallelGC
新生代:一般年轻带用复制算法,老年代用标记清除或标记整理
Serial Copying 复制算法即UseSerialGC 串行垃圾回收器
Parallerl Scavenge 即UseParallelGC串行垃圾回收器
ParNew在它们两个之间,在新生区用并行垃圾回收器
Old Gen老年代:
Serial MSC即第七种Serial Old被废弃了;
Parallel Compacting 用的Parallel Old即UseParallelOldGC并行
CMS用的并发标记清除UseConcMarkSweepGC并发
G1它们都可以用;
DefNew--->Default New Generation
Tenured--->Old
ParNew--->Parallel New Generation
PSYoungGen--->Parallel Scavenge
ParOldGen ---> Parallel Old Generation
Server/ Client模式分别是什么意思?
适用范围:只掌握Server模式即可,Client模式基本不会用;
操作系统:32位操作系统,不论硬件如何都默认使用Client的JVM模式,2G内存同时有2个cpu以上用Server模式,低于该配置还是Client模式
64位only server模式;
新生代:
- 串行GC(Serial)/ (Serial Copying)
- 并行GC(ParNew)
- 并行回收GC(Parallel) / (Parallel Scavenge)
串行GC(Serial)/ (Serial Copying) ;要么干活要么收集;需要暂停
串行收集器:Serial收集器;一句话:一个单线程的收集器,在进行垃圾收集时候,必须暂停其他所有的工作线程直到它收集结束;
串行收集器是最古老,最稳定以及效率高的收集器,只使用一个线程去回收但其在进行垃圾收集过程中可能会产生较长的停顿("Stop-The-World"状态)。
虽然在收集垃圾过程中需要暂停所有其他的工作线程,但是它简单高效,对于限定单个CPU环境来说,没有线程交互的开销可以获得最高的单线程垃圾收集效率,
因此Serial垃圾收集器依然是java虚拟机运行在Clent模式下默认的新生代垃圾收集器;
对应JVM参数是: -XX:+UseSerialGC
开启后会使用:Serial(Young区用)+Serial Old(Old区用)的收集器组合;
表示:新生代、老年代都会使用串行回收收集器,新生代使用复制算法,老年代使用标记--整理算法;
-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseSerialGC
1.-XX:UseSerialGC 串行 -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseSerialGC //(DefNew + Tenured)即新生代 + 老年代 -XX:InitialHeapSize=10485760 -XX:MaxHeapSize=10485760 -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseSerialGC *********GCDemo Hello [GC (Allocation Failure) [DefNew: [Full GC (Allocation Failure) [Tenured: Heap def new generation total 3072K, used 164K [0x00000000ff600000, 0x00000000ff950000, 0x00000000ff950000) eden space 2752K, 5% used [0x00000000ff600000, 0x00000000ff6290d8, 0x00000000ff8b0000) from space 320K, 0% used [0x00000000ff8b0000, 0x00000000ff8b0000, 0x00000000ff900000) to space 320K, 0% used [0x00000000ff900000, 0x00000000ff900000, 0x00000000ff950000) tenured generation total 6848K, used 4673K [0x00000000ff950000, 0x0000000100000000, 0x0000000100000000) the space 6848K, 68% used [0x00000000ff950000, 0x00000000ffde06f0, 0x00000000ffde0800, 0x0000000100000000) Metaspace used 3497K, capacity 4496K, committed 4864K, reserved 1056768K class space
并行GC(ParNew--并行收集器)
一句话:使用多线程进行垃圾回收,在垃圾收集时,会Stop-the-World暂停其他所有的工作线程直到它收集结束;
ParNew收集器其实就是Serial收集器新生代的并行多线程版本,最常见的应用场景是配合老年代的CMS GC工作,其余的行为和Serial收集器完全一样,ParNew垃圾收集器在垃圾收集过程中同样也要暂停所有其他的工作线程。它是很多java虚拟机运行在Server模式下新生代的默认垃圾收集器。
常用对应JVM参数: -XX:+UseParNewGC 启用ParNew收集器,只影响新生代的收集,不影响老年代。
开启上述参数后,会使用:ParNew(Young区用)+Serial Old的收集器组合,新生代使用复制算法,老年代采用标记--整理算法;
在新生代收集多一些,养老区不变还是一个
-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+UseParNewGC
备注:-XX:ParallelGCThreads 限制线程数量,默认开启和CPU数目相同的线程数;
2.-XX:+UseParNewGC 并行收集器 -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParNewGC // ParNew + Tenured 新生代+老年代-XX:InitialHeapSize=10485760 -XX:MaxHeapSize=10485760 -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParNewGC *********GCDemo Hello [GC (Allocation Failure) [ParNew: 新生代从串行变成了并行收集 [Full GC (Allocation Failure) [Tenured 还是一个
但是,ParNew+Tenured这样的搭配,java8已经不再被推荐; Java HotSpot(TM) 64-Bit Server VM warning: Using the ParNew young collector with the Serial old collector is deprecated and
will likely be removed in a future release
并行回收GC(Parallel)/ (Parallel Scavenge)
Parallel Scavenge收集器类似ParNew也是一个新生代垃圾收集器,使用复制算法,也是一个并行的多线程的垃圾收集器,俗称吞吐量优先收集器。
一句话:串行收集器在新生代和老年代的并行化;
java8默认的收集器;
它重点关注的是:可控制的吞吐量(Thoughput=运行用户代码时间 / (运行用户代码时间+垃圾收集时间),也即比如程序运行100min,垃圾收集时间1min,吞吐量就是99%)
高吞吐量意味着高效利用CPU的时间,它多用于在后台运算而不需要太多交互的任务。
自适应调节策略也是Parallel Scavenge收集器与ParNew收集器的一个重要区别。
(自适应调节策略:虚拟机会根据当前系统的运行情况收集收集性能监控信息,动态调整这些参数以提供最合适的停顿时间(-XX:MaxGCPauseMilis)或最大的吞吐量。)
常用JVM参数:-XX:+UseParallelGC 或者 -XX:+UseParallelOldGC(可互相激活) 使用Parallel Scanvenge收集器
-XX:+ParallelGCThreads=数字N 表示启动多少个GC线程; CPU>8 N=5/8; CPU<8 N=实际个数
-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+UseParallelGC
-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+UseParallelOldGC
3 -XX:+UseParallelGC 并行
-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParallelGC // PSYoungGen + ParOldGen 新生代 + 老年代 -XX:InitialHeapSize=10485760 -XX:MaxHeapSize=10485760 -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC *********GCDemo Hello [GC (Allocation Failure) [PSYoungGen: [Full GC (Ergonomics) [PSYoungGen:
老年代:
- 串行GC(Serial Old)/ (Serial MSC)
- 并行GC(Parallel Old)/ (Parallel MSC)
- 并发标记清除GC(CMS)
并行GC(Parallel Old)/ (Parallel MSC)
Parallel Old收集器是Parallel Scavenge的老年代版本,使用多线程的标记--整理算法,Paralle Old收集器在JDK1.6才开始提供。
在JDK1.6之前,新生代使用ParallelScavenge收集器只能搭配老年代的Serial Old收集器,只能保证新生代的吞吐量优先,无法保证整体的吞吐量。在JDK1.6之前(ParallelScavenge + Serial Old);
Parallel Old 正是为了在老年代同样提供吞吐量优先的垃圾收集器,如果系统对吞吐量要求比较高,JDK1.8后可以优先考虑新生代ParallelScavenge和老年代Parallel Old收集器的搭配策略。在JDK1.8及后(ParallelScavenge+Parallel Old)
JVM常用参数:-XX:+UseParallelOldGC 使用Parallel Old收集器,设置该参数后,新生代Parallel+老年代Parallel Old
4. -XX:+UseParallelOldGC -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParallelOldGC // PSYoungGen + ParOldGen -XX:InitialHeapSize=10485760 -XX:MaxHeapSize=10485760 -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelOldGC *********GCDemo Hello [GC (Allocation Failure) [PSYoungGen: [Full GC (Ergonomics) [PSYoungGen: 4.2 -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags //不加就是默认UseParallelGC //PSYoungGen + ParOldGen -XX:InitialHeapSize=10485760 -XX:MaxHeapSize=10485760 -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC *********GCDemo Hello [GC (Allocation Failure) [PSYoungGen: [Full GC (Ergonomics) [PSYoungGen: 1875K->0K(2560K)] [ParOldGen:
并发标记清除:
它有种落地的实现叫CMS
并发标记清除GC(CMS)
CMS收集器(Concurrent Mark Sweep:并发标记清除)是一种以获取最短回收停顿时间为目标的收集器。
适用在互联网或者B/S系统的服务器上,这类应用尤其重视服务器的响应速度,希望系统停顿时间最短。
CMS非常适合堆内存大、CPU核数多的服务器应用,也是G1出现之前大型应用的首选收集器。
Concurrent Mark Sweep 并发标记清除,并发收集低停顿,并发指的是与用户线程一起执行。
开启该收集器的JVM参数: -XX:+UseConcMarkSweepGC,开启该参数后会自动将 -XX:+UseParNewGC打开。
开启该参数后,使用ParNew(Young区用)+CMS(Old区用)+Serial Old的收集器组合,Serial Old将作为CMS出错的后备收集器。
-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+UseConcMarkSweepGC
5. 微服务 -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseConcMarkSweepGC // par new generation + concurren -XX:InitialHeapSize=10485760 -XX:MaxHeapSize=10485760 -XX:MaxNewSize=3497984 -XX:MaxTenuringThreshold=6 -XX:NewSize=3497984 -XX:OldPLABSize=16 -XX:OldSize=6987776 -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC -XX:-UseLargePagesIndividualAllocation -XX:+UseParNewGC *********GCDemo Hello [GC (Allocation Failure) [ParNew: [GC (CMS Initial Mark) [1 CMS-initial-mark: [Full GC (Allocation Failure) [CMS: [GC (CMS Final Remark) [YG occupancy: [CMS-concurrent-sweep: 0.000/0.000 secs] Heap par new generation total 3072K, used 164K [0x00000000ff600000, 0x00000000ff950000, 0x00000000ff950000) eden space 2752K, 5% used [0x00000000ff600000, 0x00000000ff6290c8, 0x00000000ff8b0000) from space 320K, 0% used [0x00000000ff8b0000, 0x00000000ff8b0000, 0x00000000ff900000) to space 320K, 0% used [0x00000000ff900000, 0x00000000ff900000, 0x00000000ff950000) concurrent mark-sweep generation total 6848K, used 4671K [0x00000000ff950000, 0x0000000100000000, 0x0000000100000000) Metaspace used 3497K, capacity 4496K, committed 4864K, reserved 1056768K class space used 382K, capacity 388K, committed 512K, reserved 1048576K
4步过程:
初始标记CMS initial mark,只是标记一下GC Roots能直接关联的对象,速度很快,仍然需要暂停所有的工作线程;
并发标记SMS concurrent mark 和用户线程一起,进行GC Roots跟踪的过程,和用户线程一起工作,不需要暂停工作线程。主要标记过程,标记全部对象;(和顾客一块工作,为不扫地了,没有灰尘为去擦窗户了;)
重新标记CMS remark,为了修正在并发标记期间,因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,仍然需要暂停所有的工作线程。由于并发标记时,用户线程仍然运行,因此在正式清理前,再做修正;
并发清除CMS concurrent sweep 和用户线程一起,清除GC Roots不可达对象,和用户线程一起工作,不需要暂停工作线程。基于标记结果,直接清除对象;由于耗时最长的并发标记和并发清除过程中,垃圾收集器线程可以和用户现在一起并发工作,所以总体上来看CMS收集器的内存回收和用户线程上一起并发地执行;
优缺点:
优--并发收集低停顿;
缺点:
①并发执行,对CPU资源压力大;由于并发进行,CMS在收集与应用线程会同时增加对堆内存的占用,即CMS必须要在老年代堆内存用尽之前完成垃圾回收,否则CMS回收失败时,将触发担保机制,串行老年代收集器将会以STW的方式进行一次GC,从而造成较大停顿时间。
②采用的标记清除算法会导致大量碎片;它无法整理空间碎片,老年代空间会随着应用时长被逐步耗尽,最后将不得不通过担保机制对堆内存进行压缩。
CMS也提供了参数-XX:CMSFullGCsBeForeCompaction(默认0,即每次都进行内存整理)来指定多少次CMS收集之后,进行一次压缩的Full GC.
了解
G1
以前收集器特定:
- 年轻代和老年代是各自独立且连续的内存块;
- 年轻代收集器使用单eden+S0+S进行复制算法;
- 老年代收集必须扫描整个老年代区域;
- 都是以尽可能少而快速地执行GC为设计原则。
G1(Garbage-First)收集器,是一款面向服务端应用的收集器;
G1是一种服务器端的垃圾收集器,应用在多处理器和大容量内存环境中,在实现高吞吐量的同时,尽可能的满足垃圾收集暂停时间的要求。另外,它还具有以下特性:
像CMS收集器一样能与应用程序线程并发执行;
整理空闲空间更快; 需要更多的时间来预测GC停顿时间;不希望牺牲大量的吞吐性能; 不需要更大的Java Heap;
G1收集器的设计目标是取代CMS收集器,它同CMS相比,在以下方面表现的更加出色:
G1是一个有整理内存过程的垃圾收集器,不会产生很多内存碎片;G1的Stop The World(STW)更可控,G1在停顿时间上添加了预测机制,用户可以指定期望停顿时间。
特点:
底层原理:
最大好处是化整为零,避免全内存扫描,只需要安装区域来进行扫描即可;
①Region区域化垃圾收集器
G1算法将堆划分为若干区域Region,它仍然属于分代收集器;
这些Region的一部分包含新生代,新生代的垃圾收集依然采用暂停所有应用线程的方式,将存活对象拷贝到老年代或者Survivor空间。
这些Region的一部分包含老年代,G1收集器通过将对象从一个区域复制到另外一个区域,完成了清理工作。这就意味着,在正常的处理过程中,G1完成了堆的压缩(至少是部分堆的压缩),这样也就不会有CMS内存碎片问题的存在了。
在G1中,还要一种特殊区域,叫Humongous(巨大的)区域,如果一个对象占用的空间超过了分区容量50%以上,G1收集器就认为这是一个巨型对象。这些巨型对象默认直接会被分配在老年代,但是如果它是一个短期存在的巨型对象,就会对垃圾收集器造成负面影响。为了解决这个问题,G1划分了一个Humongous区,它用来专门存放巨型对象。如果一个H区装不下一个巨型对象,那么G1会寻找连续的H分区来存储。为了能找到连续的H区,有时候部分不启动Full GC。
②回收步骤:
③四步过程
6.G1 -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseG1GC -XX:InitialHeapSize=10485760 -XX:MaxHeapSize=10485760 -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseG1GC -XX:-UseLargePagesIndividualAllocation *********GCDemo Hello [GC pause (G1 Humongous Allocation) (young) (initial-mark) (to-space exhausted), 0.0078791 secs] [GC concurrent-mark-start] Heap garbage-first heap total 10240K, used 4668K [0x00000000ff600000, 0x00000000ff700050, 0x0000000100000000) region size 1024K, 1 young (1024K), 0 survivors (0K) Metaspace used 3496K, capacity 4496K, committed 4864K, reserved 1056768K class space used 382K, capacity 388K, committed 512K, reserved 1048576K [GC pause (G1 Humongous Allocation) (young) (initial-mark) 初始标记 [GC concurrent-root-region-scan-start] [GC concurrent-mark-start] [GC concurrent-mark-end, 0.0002370 secs] [GC remark [Finalize Marking, 0.0002173 secs] [GC Worker Start (ms): Min: 141.2, Avg: 141.2, Max: 141.3, Diff: 0.1] [Ext Root Scanning (ms): Min: 0.0, Avg: 0.4, Max: 2.7, Diff: 2.7, Sum: 3.3] [Update RS ( ...
常用配置参数(了解):
开发人员仅仅需要声明以下参数即可:
三部归纳:开始G1+设置最大内存+设置最大停顿时间
-XX:+UseG1GC -Xmx32g -XX:MaxGCPauseMillis=100
-XX:MaxGCPauseMillis = n:最大GC停顿时间单位毫秒,这是个软目标,JVM将可能(但不能保证)停顿小于这个时间。
和CMS相比的两个优势:
1)G1不会产生内存碎片;
2)是可以精确控制停顿。该收集器是把整个堆(新生代、老年代)划分成多个固定大小的区域,每次根据运行停顿的时间去收集垃圾最多的区域。