垃圾回收器
垃圾回收器(GarbageCollector),对象存在被引用称为活对象,而没有被引用的对象称为垃圾对象/垃圾内存,找到垃圾对象并回收是Collector的主要工作,该过程称为垃圾回收(Garbage
Collection)。
Collector的职责:分配内存,保证有引用的内存不被释放,回收没有指针引用的内存。
Hotspot提供三类垃圾回收器:串行回收器(SerialCollector),并行回收器(Parallel
Collector),并行标志扫描垃圾回收器(CMS,ConcurrentMarkSweep Collector),G1回收器(G1
Collector)。
串行回收器是JVM在client模式下默认的GC方式,可以通过JVM配置参数-XX:
UseSerialGC来指定GC使用该回收算法。串行回收器主要有两个特点:第一,它仅仅使用单线程进行垃圾回收;第二,它独占式的垃圾回收。在串行收集器进行垃圾回收时,Java
应用程序中的线程都需要暂停,等待垃圾回收的完成,这样给用户体验造成较差效果。虽然如此,串行回收器却是一个成熟、经过长时间生产环境考验的极为高效的回收器。
并行回收器只是简单地将串行回收器多线程化,Parallel
GC根据Minor GC和Full
GC的不同分为三种,分别是ParNewGC、ParallelGC和ParallelOldGC.
ParNewGC可以通过-XX:
UseParNewGC参数来指定。ParallelGC是JVM在server模式(在64位模式下的JVM和OS中,默认会使用-server参数)下默认的GC方式,可以同JVM配置参数-XX:
UseParallelGC参数来指定GC使用该回收算法。ParallelOldGC可以通过-XX:
UseParOldGC参数来指定。他们的并发回收的线程数可以通过-XX:ParallelGCThreads来指定。ParallelOldGC与ParallelGC的不同之处在于Full
GC,前者Full
GC进行的动作为清空整个Heap堆得垃圾对象,清楚Perm区中已经被卸载的类信息,并进行压缩。而后者是清楚Heap堆中的部分来及对象,并进行部分的空间压缩。
它的回收策略、算法以及参数和串行回收器一样。并行回收器也是独占式的回收器,在收集过程中,应用程序会全部暂停。但由于并行回收器使用多线程进行垃圾回收,因此,在并发能力比较强的
CPU 上,它产生的停顿时间要短于串行回收器,而在单 CPU
或者并发能力较弱的系统中,并行回收器的效果不会比串行回收器好,由于多线程的压力,它的实际表现很可能比串行回收器差。
CMS回收器,可通过-XX:
UseConcMarkSweepGC来指定,并发的默认线程数为4,也可通过ParallelCMSThreads来指定,与并行回收器不同,CMS
收集器主要关注于系统停顿时间。CMS 是 Concurrent Mark Sweep
的缩写,意为并发标记清除,从名称上可以得知,它使用的是标记-清除算法,同时它又是一个使用多线程并发回收的垃圾回收器。
CMS工作的主要步骤有
①CMS-initial-mark,单线程处理环节,并且会完全暂停JVM,找到所有的root根对象(栈中引用的对象,本地方法栈中引用的对象,常量引用的对象),将root根所引用的对象标志在一个在一个bitmap钟,此时业务恢复正常运行,CMS进入下一步骤
②CMS-concurrent-mark,根据第1个步骤对记录在Bitmap中的信息进行并发标志对象,此时JVM内所运行的程序不会暂停。
③CMS-concurrent-preclean,处理young与old之间的引用,尽量将可能的引用梳理出来,节约remark的时间
④CMS-consurrent-abortable-preclean
⑤CMS-remark,JVM全暂停的第2个阶段,由于全面几个步骤中存在的业务并发导致一些赞数据,remark解决了这个问题,因此这个阶段也会全部暂停,但是它不是从root开始遍历,而是根据一个卡表(卡表中有脏数据的信息)。
⑥CMS-concurrent-sweep,不停机,单线程操作,按照内存的地址顺序遍历old、perm区域,根据Bitmap中的记录,对垃圾内存进行清除操作。
⑦CMS-consurrent-reset
CMS 回收器在其主要的工作阶段虽然没有暴力地彻底暂停应用程序线程,但是由于它和应用程序线程并发执行,相互抢占
CPU,所以在 CMS 执行期内对应用程序吞吐量造成一定影响。CMS 默认启动的线程数是
(ParallelGCThreads 3)/4),ParallelGCThreads
是新生代并行收集器的线程数,也可以通过-XX:ParallelCMSThreads 参数手工设定 CMS 的线程数量。当
CPU
资源比较紧张时,受到 CMS 收集器线程的影响,应用程序的性能在垃圾回收阶段可能会非常糟糕。
由于 CMS 回收器不是独占式的回收器,在 CMS
回收过程中,应用程序仍然在不停地工作。在应用程序工作过程中,又会不断地产生垃圾。这些新生成的垃圾在当前 CMS
回收过程中是无法清除的。同时,因为应用程序没有中断,所以在 CMS
回收过程中,还应该确保应用程序有足够的内存可用。因此,CMS
回收器不会等待堆内存饱和时才进行垃圾回收,而是当前堆内存使用率达到某一阈值时,便开始进行回收,以确保应用程序在 CMS
工作过程中依然有足够的空间支持应用程序运行。
这个回收阈值可以使用-XX:CMSInitiatingOccupancyFraction 来指定,默认是
68。即当老年代的空间使用率达到 68%时,会执行一次 CMS 回收。如果应用程序的内存使用率增长很快,在 CMS
的执行过程中,已经出现了内存不足的情况,此时,CMS 回收将会失败,JVM
将启动串行回收器进行垃圾回收。如果这样,应用程序将完全中断,直到垃圾回收完成,这时,应用程序的停顿时间可能很长。因此,根据应用程序的特点,可以对-XX:CMSInitiatingOccupancyFraction
进行调优。如果内存增长缓慢,则可以设置一个稍大的值,大的阈值可以有效降低 CMS
的触发频率,减少老年代回收的次数可以较为明显地改善应用程序性能。反之,如果应用程序内存使用率增长很快,则应该降低这个阈值,以避免频繁触发串行回收器。
G1回收器在Hotspot上只算是一个雏形,适用于堆内存很大的情况,通过JVM参数 –XX: UseG1GC
使用G1垃圾回收器。G1的基本思想是将Java堆划分为多个相对较小且大小想用的内存板块,这些板块我们叫做region。它也有分代啊,在region里分代,在运行过程中会维护一个优先级列表,这个优先级列表其实是按照垃圾多少来排序的,这样垃圾最多的region就是最前面的一个板块,每次就回收垃圾最多的板块。
Java内存日志分析
我们不知道什么时候回发生内存泄漏,但是我们可以在JVM启动时加上一些参数进行控制,当JVM出现问题的时候可以记录下当时的情况,还有记录下来的GC的日志。
GC的输出配置参数如下:
-verbose:gc,可以辅助输出一些详细的GC信息。
-XX:
PrintGCDetails,输出GC的详细信息。
-XX:
PrintGCApplicationStoppedTime,输出GC造成应用程序暂停的时间。
-XX:
PrintGCDateStamps,GC发生的时间信息
-XX:
PrintHeapAtGC,在GC前后输出堆中各个区域的大小
-Xloggc:[file],将GC信息输出到单独的文件中
除了CMS的日志与其他GC的日志差别较大外,每种GC的日志形式都可以抽象成如下格式:
[GC [:->(total size1), secs]
->(total size2), secs]
q其中说明如下:
,表示收集器的名称。
,表示young区在GC前占用的内存。
,表示young区在GC后占用的内存。
,表示young区局部收集时JVM暂停处理的时间。
,表示JVM
Heap在GC前占用的内存。
<
style="line-height: 21px; text-indent:
2em;">ending occupancy2>,表示JVM Heap在GC后占用的内存
,表示GC过程中JVM暂停处理的总时间。
可以根据日志来判断是否有内存泄漏,如果
-
=<
endingoccupancy2>
-,表明这次GC对象100%倍回收,没有对象进入Old区或者Perm区。如果等号前面的值大于等号后面的值,那么差值就是这次回收对象进入Old区或者是Perm区的大小。如果随着时间的演唱,<ending
occupancy2>的值一直在增加,而且Full
GC很频繁,那么很可能就是内存泄漏。
除去日志文件分析外,还可以直接通过JVM自带的一些工具分析,如jstat,使用命令jstat -gcutil [pid]
[internal] [count],结果如下:
在上面日志中的参数含义如下:
S0表示Heap上的Survivor space 0区已使用空间的百分比。
S1表示Heap上的Survivor space 1区已使用空间的百分比。
E表示Heap上的Eden
space区已使用空间的百分比。
O表示Heap上的Old
space区已使用空间的百分比。
M表示元数据区已使用空间的百分比。
CCS表示压缩使用比例。
YGC表示应用从应用程序启动到采样时发生Young GC的次数
YGCT表示从应用程序启动到采样时Young GC所用的时间(秒)
FGC表示从应用程序启动到采样时Full GC的次数。
FGCT表示从应用程序启动到采样时Full GC所用的时间(秒)
GCT表示从应用程序启动到采样时用于垃圾回收的总时间(秒)
除此之外,还可以使用堆快照文件分析,可通过命令jmap -dump:format=b,file=[filename]
[pid]来记录下堆得内存快照,然后利用第三方工具(mat)来分析整个Heap的情况。如果内存耗尽那么可能导致JVM直接垮掉,可以通过参数:-XX:
HeapDumpOnOutOfMemoryError来配置当内存耗尽时记录下内存快照。