一、CMS回收器(低延迟)
1、CMS回收器概述
1、CMS(Concurrent Mark Sweep)收集器是
HosSpot虚拟机中第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程同时工作
。2、CMS收集器的关注点是
尽可能缩短垃圾收集时用户线程的停顿时间
。停顿时间越短(低延迟)就越适合与用户交互的程序,良好的响应速度能提升用户体验。3、
目前很大一部分的Java应用集中在互联网网站或者基于浏览器的B/S系统的服务端上,这类应用通常都会较为 关注服务的响应速度,希望系统停顿时间尽可能短
,以给用户带来良好的交互体验。CMS收集器就非 常符合这类应用的需求。4、CMS的垃圾收集算法采用
标记-清除
算法,也会STW。5、不幸的是,CMS作为老年代的收集器,却无法与JDK1.4.0中已经存在的新生代收集器Parallel Scavenge配合工作,所以在JDK1.5中使用CMS来收集老年代的时候,新生代只能选择ParNew或者Serial收集器中的一个。
6、在G1出现之前,CMS使用还是非常广泛的。一直到今天,仍然有很多系统使用CMS GC。
2、CMS的工作原理
1、CMS运作过程相对于前面几种收集器来说要更复杂一些,整个过程分为
四个步骤
:2、
初始标记(Initial-Mark)阶段
:在这个阶段中,程序中所有的工作线程都将会因为“Stop-The-World”机制而出现短暂的暂停,这个阶段的主要任务仅仅只是标记出GC Roots能直接关联到的对象
。一旦标记完成之后就会恢复之前被暂停的所有应用线程。由于直接关联对象比较小,所以这里的速度非常快
。3、
并发标记(Concurrent-Mark)阶段
:从GC Roots的直接关联对象开始遍历整个对象图的过程
,这个过程耗时较长但是不需要停顿用户线程
,可以与垃圾收集线程一起并发运行
。4、
重新标记(Remark)阶段
:由于在并发标记阶段中,程序的工作线程会和垃圾收集线程同时运行或者交叉运行,因此为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录
(因为并发标记和用户线程同时执行,可能导致已经标记的可达对象,在用户执行过程中改变为不可达,所以需要重新标记阶段),这个阶段的停顿时间通常会比初始标记阶段稍长一些
,并且也会导致“Stop-The-World”的发生,但也远比并发标记阶段的时间短。5、
并发清除(Concurrent-Sweep)阶段
:此阶段清理删除掉标记阶段判断的已经死亡的对象,释放内存空间
。由于不需要移动存活对象(因为是标记清除算法)
,所以这个阶段也是可以与用户线程同时并发的。
3、CMS的特点
1、尽管CMS收集器采用的是并发回收(非独占式),但是在其
初始化标记和重新标记这两个阶段中仍然需要执行STW机制暂停程序中的工作线程
,不过暂停时间并不会太长,因此可以说明目前所有的垃圾收集器都做不到完全不需要STW机制,只是尽可能地缩短暂停时间。2、
由于最耗费时间的并发标记与并发清除阶段都不需要暂停工作,所以整体的回收是低停顿的
。3、由于在垃圾收集阶段用户线程没有中断,所以
在CMS回收过程中,还应该确保应用程序用户线程有足够的内存可用
。4、因此,CMS收集器不能像其他收集器那样等到老年代几乎完全被填满了再进行垃圾收集,而是
当堆内存使用率达到某一阈值时,便开始进行回收,以确保应用程序在CMS工作过程中依然有足够的空间支持应用程序运行
。5、要是CMS运行期间预留的内存无法满足程序需要,就会出现一次
Concurrent Mode Failure
失败,这是虚拟机将不得不启动后备预案:冻结用户线程的执行,临时启用Serial Old收集器
来重新进行老年代的垃圾收集,这样停顿时间就很长了。6、CMS收集器的垃圾收集算法采用的是
标记-清除算法
,这意味着每次执行完内存回收后,由于被执行内存回收的无用对象所占用的内存空间极有可能是不连续的一些内存块,不可避免地将会产生一些内存碎片
。那么CMS在为新对象分配内存空间时,将无法使用指针碰撞(Bump the Pointer)技术,而只能够选择空闲列表(Free List)执行内存分配
。
4、使用标记清除算法而不是标记压缩算法原因
1、因为当并发清除的时候,用标记压缩整理内存的话,原来的用户线程正在使用的内存,没办法通过移动存活对象的地址,因为正在使用这些对象,地址无法被改变。
2、要保证用户线程能继续执行,前提的它运行的资源不受影响。
标记压缩更适合Stop The World
这种场景下使用(因为STW的时候,用户线程就不运行了,此时可以整理存活对象的内存,解决内存碎片问题)。
5、CMS优缺点
1、优点:
2、缺点:
CMS收集器对CPU资源非常敏感
。事实上,面向并发设计的程序都对CPU资源比较敏感。在并发阶段,它虽然不会导致用户线程停顿,但却会因为占用了一部分线程(或者说CPU的计算能力)而导致程序变慢,降低总吞吐量
。CMS默认启动的回收线程数是(CPU核心数量 + 3)/ 4
,也就是说,如果核心数在4个或以上,并发回收时垃圾收集线程只占用不超过25%的CPU运算资源,并且会随着CPU核心数量的增加而下降。如果核心数不足4个时,CMS对用户线程的影响就可能变得很大,如果应用本来的CPU负载很高,还要分出一半的运算能力去执行垃圾收集线程,就可能导致用户程序的执行速度大幅降低。
CMS收集器无法处理浮动垃圾(Floating Garbage)
,有可能出现Concurrent Mode Failure
失败而导致另一次完全Stop The World
的Full GC
产生。在CMS的并发标记和并发清理阶 段,用户线程是还在继续运行的,程序在运行自然就还会伴随有新的垃圾对象不断产生,但这一部分垃圾对象是出现在标记过程结束以后,CMS无法在当次收集中处理掉它们,只好留待下一次垃圾收集时再清理掉。这一部分垃圾就称为浮动垃圾
。
会产生内存碎片
,CMS是基于标记-清除算法
实现的收集器,在收集结束时会有大量的空间碎片产生。空间碎片过多时,将会给大对象分配带来很大麻烦,往往会出现老年代还有很多剩余空间,但就是无法找到足够大的连续空间来分配当前对象,而不得不提前触发一次Full GC
的情况。
6、CMS收集器常用JVM参数
1、设置JVM启动参数
指定老年代使用CMS收集器执行内存回收任务
:-XX:+UseConcMarkSweepGC
2、设置JVM启动参数
指定堆内存使用率的阈值,一旦达到该阈值,便开始回收
:-XX:CMSInitiatingOccupancyFraction
JDK5及以前版本的默认值为68,即当老年代空间使用率达到68%时,会执行一次CMS回收。
JDK6及以上版本默认值为92%
。如果内存使用率增长缓慢,可以适当调高该参数的值来提高CMS的触发百分比,降低内存回收频率,减少老年代回收次数,获取更好的性能。
如果内存使用率增长很快,则应该降低该参数的值,以避免频繁触发老年代串行收集器。因此
通过该参数便可以有效降低Full GC的执行次数
。3、设置JVM启动参数
指定在CMS收集器不得不进行Full GC时开启内存碎片的整理(对内存空间进行压缩整理)
:-XX:+UseCMSCompactAtFullCollection
4、设置JVM启动参数
指定在执行多少Full GC后对内存空间进行压缩整理
:-XX:CMSFullGCsBeforeCompaction
5、设置JVM启动参数
指定CMS收集器线程的数量
:-XX:ParallelCMSThreads
7、如何选择回收器
1、HotSpot有这么多的垃圾回收器,那么如果有人问,Serial GC、Parallel GC、Concurrent Mark Sweep GC这三个GC有什么不同呢?
如果你想要最小化地使用内存和并行开销,
请选Serial GC(减少上下文切换)
。如果你想要最大化应用程序的吞吐量,
请选Parallel GC(最大化吞吐量)
。如果你想要最小化GC的中断或停顿时间,
请选CMS GC(减少STW,增高低延迟)
。2、JDK后续版本中CMS的变化
二、G1回收器(区域化分代式)
1、G1出现背景
1、由于应用程序所应对的
业务越来越庞大、复杂,用户越来越多,没有GC就不能保证应用程序正常进行,而经常造成STW的GC又跟不上实际的需求
,因此才会不断对GC进行优化。2、G1(Garbage-First)垃圾回收器是在Java7 Update 4之后引入的一个新的垃圾回收器,是当今收集器技术发展最前沿的成果之一。与此同时,为了适应现在
不断扩大的内存和不断增加的处理器数量,进一步降低暂停时间(pause time),同时兼顾良好的吞吐量
。3、官方给
G1设定的目标是在延迟可控的情况下获得尽可能高的吞吐量,所以才当起“全功能收集器”的重任与期望
。
2、G1的含义(Region分区)
1、G1是一个
并行回收器
,它把连续的Java堆内存划分为多个大小相等的独立区域(Region),使用不同的Region来表示新生代的Eden空间、Survivor空间(Survivor0区、Survivor1区),或者老年代空间
。2、G1 GC有计划地避免在整个Java堆中进行全区域的垃圾收集。G1跟踪各个Region里面的垃圾堆积的价值大小(回收所获得的空间大小及回收所需要时间的经验值),在后台维护一个
优先列表
,每次根据允许的收集时间,优先回收价值最大的Region
。3、由于这种方式的
侧重点在于回收垃圾最大量的区间(Region)
,因此G1也称为:垃圾优先(Garbage First)。4、G1(Garbage First)是一款面向服务端应用的垃圾收集器,
主要针对配备多核CPU及大容量内存的机器
,以极高概率满足GC停顿时间的同时,还兼具高吞吐量的性能特征
。5、在JDK1.7版本正式启用,移除了Experimental的标识,
是JDK9以后的默认垃圾回收器,取代了CMS回收器以及Parallel + Parallel Old的组合,被Oracle官方称为全功能的垃圾收集器
。6、G1收集器Region分区示意图
3、G1回收器的特点(优势)
与其他的GC收集器相比,G1使用了全新的分区算法,其特点如下:
1、并行与并发
并行性:G1在回收期间,可以有多个GC线程同时工作,有效利用多核计算能力。此时用户线程STW。
并发性:
G1拥有与应用程序交替执行的能力
,部分工作可以和应用程序同时执行,因此,一般不会在整个回收阶段发生完全阻塞应用程序的情况
。
2、分代收集
从分代上看,
G1仍然属于分代型垃圾回收器
,它会区分老年代和年轻代,年轻代(有Eden区和Survivor区)。但从堆的结构上看,它不要求整个Eden区、年轻代或者老年代都是连续的,也不再坚持固定大小和固定数量
。将
堆空间分为若干区域(Region),这些区域中包含了逻辑上的年轻代和老年代
。和之前的各类的回收器不同,它同时兼顾年轻代和老年代
。
G1之前的所有回收器(包括CMS在内),垃圾收集的目标范围要么是整个新生代(Minor GC),要么就是整个老年代(Major GC),再要么就是整个Java堆(Full GC),而G1可以面向堆内存任何部分来组成回收集(Collection Set,简称CSet)进行回收,衡量的标准不再是它属于哪个分代,而是哪块内存中存放的垃圾数量最多,回收收益最大(即G1收集器的Mixed GC模式)。
3、空间整合
CMS回收器的垃圾收集算法采用的是
标记-清除算法,不可避免地将会产生一些内存碎片,因此在若干次GC后进行一次碎片整理
。G1将内存划分为一个个的Region。内存回收是以Region作为基本单位。
Region之间是复制算法(Survivor0区、Survivor1区)
,但整体上实际可以看作是标记压缩算法
,这两种算法都可以避免内存碎片
。这种特性有利于程序长时间运行,分配大对象时不会因为无法找到连续内存空间而提前触发一次Full GC。尤其是当Java堆非常大的时候,G1的优势会更加明显。
4、可预测的停顿时间模型(即:软实时soft real-time)
,这是G1相对于CMS的另一大优势,G1除了追求低停顿,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗垃圾收集上的时间不得超过N毫秒。
4、G1回收器的缺点
1、相较于CMS,G1还不具备全方位、压倒性优势。 比如在用户程序运行过程中,G1无论是为了垃圾收集产生的内存占用(Footprint)还是程序运行时的额外执行负载(Overload)都要比CMS要高。
2、从经验上来说,在小内存应用上CMS的表现大概率会优于G1,而G1在大内存应用上则发挥其优势。平衡点在6-8GB之间。
5、G1回收器常用JVM参数
1、设置JVM启动参数
指定使用G1收集器执行内存回收任务
:-XX:+UseG1GC
2、设置JVM启动参数
指定每个Region的大小
:-XX:G1HeapRegionSize
3、设置JVM启动参数
设置期望达到的最大GC停顿时间
:-XX:MaxGCPauseMillis
4、设置JVM启动参数
指定STW时GC线程数的值,最多设置为8
:-XX:ParallelGCThreads
5、设置JVM启动参数
设置并发标记的线程数
:-XX:ConcGCThreads
6、设置JVM启动参数
设置触发并发GC周期的Java堆占用率阈值,超过该值就触发GC,默认是45
:-XX:InitiatingHeapOccupancyPercent
6、G1回收器的常见操作步骤
1、G1的设计原则就是简化JVM性能调优,只需要三步即可完成调优:
2、G1中提供了三种垃圾回收模式在不同条件下触发:
7、G1回收器的应用场景
1、面向服务端应用,针对具有大内存、多处理的机器(在普通大小的堆里表现并不好)。
2、最主要的应用是需要低GC延迟,并具有大堆的应用程序提供解决方案。
3、用来替换掉JDK1.5中的CMS收集器;在下面的情况时,使用G1可能比CMS好:
4、HotSpot垃圾收集器里
8、分区Region(化整为零)
1、使用G1收集器时,它将整个Java堆划分成约2048个大小相同的独立Region块,每个Region块大小根据堆空间的实际大小而定,整体被控制在1MB到32MB之间,且为2的N次幂,即1MB,2MB,4MB,8MB,16MB,32MB。
2、
-XX:G1HeapRegionSize
参数可以设定Region大小。所有的Region大小相同,且在JVM生命周期内不会被改变
。3、虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔离的了,它们都是一部分Region(不需要连续)的动态集合。通过Region的动态分配方式实现逻辑上的连续。
4、
一个Region有可能属于Eden,Survivor或者Old/Tenured内存区域
。但是一个Region只可能属于一个角色。下图中的E表示该Region属于Eden内存区域,S表示属于Survivor内存区域,O表示属于Old内存区域。图中空白的表示未使用的内存空间。5、G1垃圾收集器还增加了一种新的内存区域,叫做Humongous内存区域,如图中的H块。
主要用于存储大对象,G1认为只要大小超过单个Region容量一半的对象(即0.5个Region),就放到H。而对于那些超过整个Region容量的超大对象,将会被存放到N个连续的H之中。
6、设置Humongous内存区域原因:
对于堆中的大对象,默认直接会被分配到老年代,但是如果它是一个生命周期很短的大对象,就会对垃圾收集器造成负面影响。因为老年代垃圾收集的频率比较低,而这个短期的对象会存活很长时间,始终占有内存空间(宽泛意义上的内存泄漏)
。为了解决这个问题,G1划分了一个Humongous区,用来专门存放大对象。
如果一个H区装不下一个大对象,那么G1会寻找连续的H区来存储
。为了能找到连续的H区,有时候不得不启动Full GC。G1的大多数行为都把H区作为老年代的一部分来看待。7、Region的内部结构
三、G1回收器的回收流程
1、G1回收器回收流程的主要环节
1、主要有以下三个环节:
年轻代GC(Young GC)
老年代并发标记过程(Concurrent Marking)
混合回收(Mixed GC)
(如果需要,单线程、独占式、高强度的Full GC还是继续存在的。它针对GC的评估失败提供了一种失败保护机制,即强力回收。)
2、图示
3、详细说明
当年轻代的Eden区用尽时开始年轻代回收过程
。G1的年轻代收集阶段是一个并行
的独占式
收集器。在年轻代回收期间,G1 GC暂停所有应用程序线程,启动多线程执行年轻代回收。然后将年轻代中存活对象移动到Survivor区或者Old区
。当堆内存使用率到达一定值(默认
45%
)时,开始老年代并发标记过程。标记完成之后马上开始混合回收
过程。对于一个混合回收期,G1 GC从老年区间移动存活对象到空闲区间,这些空闲空间也就成为了老年代的一部分。和年轻代不同,老年代的G1回收器和其他GC不同,
G1的老年代回收器不需要整个老年代被回收,一次只需要扫描/回收一小部分老年代的Region就可以了
。同时,这个老年代的Region和年轻代一起被回收的。4、举例说明
2、G1回收器回收流程的记忆集
1、存在问题
一个对象被不同区域引用的问题
一个Region不可能是孤立的,一个Region中的对象可能被其他任意Region中的对象引用,判断对象存活时,是否需要扫描整个Java堆才能保证准确。
在其他的分代收集器,也存在这样的问题(而G1更突出,因为G1主要针对大堆,如果通过扫描整个堆来判断,代价太大)
2、
记忆集Remembered Set是一种用于记录从非收集区域指向收集区域的指针集合的抽象数据结构,可以避免把整个老年代加进GC Roots扫描范围
。3、解决方法
无论G1还是其他分代收集器,JVM都是使用Remembered Set来避免全局扫描。
每个Region都有一个对应的Remembered Set
每次Reference类型数据写操作时,都会产生一个写屏障(Write Barrier)暂时中断操作;
然后检查将要写入的引用指向的对象是否和该Reference类型数据在不同的Region(其他收集器,检查老年代对象是否引用了新生代对象);
如果不同,通过Card Table把相关引用信息记录到引用指向对象的所在Region对应的Remembered Set中;
当进行垃圾收集时,在GC根节点的枚举范围加入Remembered Set,就可以保证不进行全局扫描,也不会有遗漏。
3、G1年轻代GC
1、JVM启动时,G1先准备好Eden区,程序在运行过程中不断创建对象到Eden区,
当Eden空间耗尽时,G1会启动一次年轻代垃圾回收过程
。年轻代垃圾回收只会回收Eden区和Survivor区
。2、YGC时,首先
G1停止应用程序的执行(STW)
,G1创建回收集(Collection Set),回收集是指需要被回收的内存分段的集合,年轻代回收过程的回收集合包含年轻代Eden区和Survivor区所有的内存分段
。
4、G1年轻代GC回收过程
1、第一阶段,扫描根(GCRoots)寻找可达对象
2、第二阶段,更新记忆集
3、第三阶段,处理记忆集
4、第四阶段,复制对象。(Eden中的对象复制到Survivor中)
此阶段,对象树被遍历,
Eden区内存分段中存活的对象会被复制到Survivor区中空的内存分段,Survivor区内存分段中存活的对象会判断对象年龄的阈值
。
如果年龄未达到阈值,年龄会加1,达到会被复制到Old区中空的内存分段
。
如果Survivor内存空间不足,Eden空间中的部分数据会直接晋升到Old空间中
。5、第五阶段,处理引用
处理Soft,Weak,Phantom,Final,JNI Weak 等引用
。最终Eden空间的数据为空,GC停止工作,而目标内存中的对象都是连续存储的,没有碎片,所以复制过程可以达到内存整理的效果,减少碎片。备注:
5、G1并发标记过程 (和CMS流程类似)
1、初始标记阶段
2、根区域扫描(Root Region Scanning)
3、并发标记(Concurrent Marking)
在整个堆中进行并发标记(和应用程序并发执行),此过程可能被YGC中断。
在并发标记阶段,
若发现区域对象中的所有对象都是垃圾,那这个区域会被立即回收
。同时并发标记过程中,会计算每个区域的对象活性(区域中存活对象的比例)。
4、再次标记(Remark)
5、独占清理(Clean UP)
6、并发清理阶段
6、G1混合回收概述
1、当越来越多的对象晋升到老年代Old Region时,为了避免堆内存被耗尽,虚拟机会触发一个混合的垃圾收集器,即Mixed GC,
该算法并不是一个Old GC,除了回收整个Young Region,还会回收一部分的Old Region
。2、这里需要注意:
是一部分老年代,而不是全部老年代
。可以选择哪些Old Region进行收集,从而可以对垃圾回收的耗时时间进行控制。也要注意的是Mixed GC并不是Full GC
。
7、G1混合回收过程
1、并发标记结束以后,老年代中百分百为垃圾的内存分段被回收了,部分为垃圾的内存分段被计算了出来。
2、默认情况下,这些老年代的内存分段会分8次(可以通过-XX:G1MixedGCCountTarget设置)被回收。
3、混合回收的回收集(Collection Set)包括八分之一的老年代内存分段,Eden区内存分段,Survivor区内存分段。混合回收的算法和年轻代回收的算法完全一样,只是回收集多了老年代的内存分段。具体过程请参考上面的年轻代回收过程。
4、由于老年代中的内存分段默认分8次回收,G1会优先回收垃圾多的内存分段。
垃圾占内存分段比例越高的,越会被先回收
。并且有一个阈值会决定内存分段是否被回收,-XX:G1MixedGCLiveThresholdPercent,默认为65%,意思是垃圾占内存分段比例要达到65%才会被回收。如果垃圾占比太低,意味着存活的对象占比高,在复制的时候会花费更多的时间。5、混合回收并不一定要进行8次。有一个阈值-XX:G1HeapWastePercent,默认值为10%,意思是允许整个堆内存中有10%的空间被浪费,意味着如果发现可以回收的垃圾占堆内存的比例低于10%,则不再进行混合回收。因为GC会花费很多的时间但是回收到的内存却很少。
8、G1回收器回收可选过程Full GC
1、G1的初衷就是要避免Full GC的出现。但是如果上述方式不能正常工作,G1会
停止应用程序的执行(STW)
,使用单线程
的内存回收算法进行垃圾回收,性能会非常差,应用程序停顿时间会很长。2、要避免Full GC的发生,一旦发生需要进行调整。什么时候会发生Full GC?
3、导致Full GC的原因可能有两个:
9、G1回收器优化建议
1、年轻代大小
2、暂停时间不要太过严苛
四、垃圾回收器总结
1、七款垃圾回收器的比较
截止JDK1.8,一共有
7
款不同的垃圾收集器。每一款的垃圾收集器都有不同的特点,在具体使用的时候,需要根据具体的情况选用不同的垃圾收集器。GC发展阶段:Serial => Parallel(并行)=> CMS(并发)=> G1 => ZGC
2、怎么选择垃圾回收器
Java垃圾收集器的配置对于JVM优化来说是一个很重要的选择,选择合适的垃圾收集器可以让JVM的性能有一个很大的提升。
五、GC日志分析
1、常用参数配置
-XX:+PrintGC
:输出GC日志。类似-verbose:gc
-XX:+PrintGCDetails
:输出GC的详细日志
-XX:+PrintGCTimeStamps
:输出GC的时间戳(以基准时间的形式)
-XX:+PrintGCDateStamps
:输出GC的时间戳(以日期的形式,如2021-10-27T21: 53: 59.234 +0800)
-XX:+PrintHeapAtGC
:在进行GC的前后打印出堆的信息
-Xloggc:./log/gc.log
:日志文件的输出路径,./表示项目的根目录
2、各参数使用说明
/**
* 测试各个日志参数
* 设置JVM启动参数:-Xms60m -Xmx600m
*/
public class GCLogTest {
public static void main(String[] args) {
ArrayList<byte[]> list = new ArrayList<>();
for (int i = 0; i < 500; i++) {
//100MB数组
byte[] b = new byte[1024 * 100];
list.add(b);
}
}
}
1、设置GC日志参数为:-XX:+PrintGC
说明:
2、设置GC日志参数为:-XX:+PrintGCDetails
说明:
GC、Full GC
:GC的类型,GC只在新生代上进行,Full GC包括永久代,新生代,老年代。如果有Full则说明GC发生了STW。
Allocation Failure
:表明本次引起GC的原因是因为在年轻代中没有足够的空间能够存储新的数据了。
PSYoungGen
:使用了Parallel Scavenge并行垃圾收集器的新生代GC前后大小的变化。
ParOldGen
:使用了Parallel Old并行垃圾收集器的老年代GC前后大小的变化。
Metaspace
:元数据区GC前后大小的变化,JDK1.8中引入元数据区以替代永久代。
x.xxxxx secs
:GC花费的时间。
Times: user
:指的是垃圾收集器花费的所有CPU时间。
sys
:花费在等待系统调用或者系统事件的时间。
real
:GC从开始到结束的时间,包括其他进程占用时间片的实际时间。
[PSYoungGen: 15263K->2536K(17920K)] 15263K->14370K(58880K)
:补充说明:user代表用户态回收耗时,sys内核态回收耗时,real实际耗时。由于多核线程切换的原因,时间总和可能会超过real时间。
使用
Serial收集器
在新生代的名字是Default New Generation,因此显示的是DefNew
使用
ParNew收集器
在新生代的名字会变成ParNew
,意思是"Parallel New Generation"使用
Parallel scavenge收集器
在新生代的名字是PSYoungGen
3、设置GC日志参数为:-XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps,日志带上了时间和日期
4、Young GC图示
5、Full GC图示
3、GC代码举例
/**
* 设置JVM启动参数:-Xms20m -Xmx20m -Xmn10m -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC
* 堆初始大小、最大大小20M;新生代大小10m;Eden:Survivor=8:2
*/
public class GCLogTest1 {
private static final int _1MB = 1024 * 1024;
public static void main(String[] args) {
testAllocation();
}
private static void testAllocation() {
byte [] allocation1,allocation2,allocation3,allocation4;
allocation1 = new byte[2 * _1MB];
allocation2 = new byte[2 * _1MB];
allocation3 = new byte[2 * _1MB];
allocation4 = new byte[4 * _1MB];
}
}
1、JDK7中的情况
首先会将3个2M的数组存放到Eden区,然后后面4M的数组来了后,将无法存储,因为Eden区只剩下2M的剩余空间了,那么将会进行一次Young GC操作,将原来Eden区的内容,存放到Survivor区,但是Survivor区也存放不下,那么就会直接晋级存入Old区。
2、JDK8中的情况
4、常用日志分析工具
常用的日志分析工具有:GCViewer、GCEasy、GCHisto、GCLogViewer、Hpjmeter、garbagecat等
推荐使用:GCViewer、GCEasy