對於jmx提供的垃圾回收器的次數,很多人的疑問點都是老年代的回收次數是否等於FGC的次數。下面我們就圍繞這個問題來進行分析。
jmx的提供以及用法
api介紹
public interface GarbageCollectorMXBean extends MemoryManagerMXBean {
public long getCollectionCount();
public long getCollectionTime();
}
jmx提供了gc次數和gc時間的接口。他們分別返回gc總次數以及gc總時間。
for (GarbageCollectorMXBean garbageCollector : ManagementFactory.getGarbageCollectorMXBeans()) {
//todo
}
使用方式也比較簡單。
認識垃圾回收器名稱
static GCMemoryManager* get_copy_memory_manager();//Copy
static GCMemoryManager* get_parnew_memory_manager();//ParNew
static GCMemoryManager* get_g1YoungGen_memory_manager();//G1 Young Generation
static GCMemoryManager* get_psScavenge_memory_manager();//PS Scavenge
static GCMemoryManager* get_psMarkSweep_memory_manager();//PS MarkSweep
static GCMemoryManager* get_g1OldGen_memory_manager();//G1 Old Generation
static GCMemoryManager* get_msc_memory_manager();//MarkSweepCompact
static GCMemoryManager* get_cms_memory_manager();//ConcurrentMarkSweep
以上就是列舉jdk1.8中的垃圾回收器以及名字,前面4個是年輕代的。後面4個是老年代的。hotspot在1.8中還都是分代回收。所以jvm啓動後會帶有2個垃圾回收器。需要根據名字來獲取對應的數據。
查看數據來源
以上的manager都是GCMemoryManager的子類。時間和次數都在父類的成員變量中,接下來的邏輯基本都是在父類中實現的。子類主要是提供名字的和算法種類。
void MemoryService::gc_end(bool fullGC, bool recordPostGCUsage,
bool recordAccumulatedGCTime,
bool recordGCEndTime, bool countCollection,
GCCause::Cause cause) {
GCMemoryManager* mgr;
if (fullGC) {
mgr = (GCMemoryManager*) _major_gc_manager;
} else {
mgr = (GCMemoryManager*) _minor_gc_manager;
}
assert(mgr->is_gc_memory_manager(), "Sanity check");
// register the GC end statistics and memory usage
mgr->gc_end(recordPostGCUsage, recordAccumulatedGCTime, recordGCEndTime,
countCollection, cause);
}
這裏有個判定,是fullgc的時候就會使用老年代垃圾回收器,次數和時間增加的邏輯就在 mgr->gc_end中。MemoryService::gc_end的調用也是很有意思的,寫在了析構函數中。
TraceMemoryManagerStats::~TraceMemoryManagerStats() {
MemoryService::gc_end(_fullGC, _recordPostGCUsage, _recordAccumulatedGCTime,
_recordGCEndTime, _countCollection, _cause);
}
現在問題就轉變成什麼時候生成TraceMemoryManagerStats的對象(對象銷燬就會調用析構函數)。下面我們開始跟蹤老年代的調用次數
MarkSweepCompact
void GenCollectedHeap::do_collection(bool full,
bool clear_all_soft_refs,
size_t size,
bool is_tlab,
int max_level) {
...
TraceMemoryManagerStats tmms(_gens[i]->kind(),gc_cause());
...
}
msc就是老年代回收的時候,調用一次。是fullgc的次數。
PS MarkSweep
PS MarkSweep是垃圾回收器的名稱,但是他有兩種算法
psMarkSweep
單線程回收
bool PSMarkSweep::invoke_no_policy(bool clear_all_softrefs) {
...
TraceMemoryManagerStats tms(true /* Full GC */,gc_cause);
...
}
psParallelCompact
多線程回收
bool PSParallelCompact::invoke_no_policy(bool maximum_heap_compaction) {
...
TraceMemoryManagerStats tms(true /* Full GC */,gc_cause);
...
}
也在老年代垃圾回收器fullgc時進行增加次數
cms
TraceCMSMemoryManagerStats::TraceCMSMemoryManagerStats(CMSCollector::CollectorState phase, GCCause::Cause cause): TraceMemoryManagerStats() {
switch (phase) {
case CMSCollector::InitialMarking:
initialize(true /* fullGC */ ,
cause /* cause of the GC */,
true /* recordGCBeginTime */,
true /* recordPreGCUsage */,
false /* recordPeakUsage */,
false /* recordPostGCusage */,
true /* recordAccumulatedGCTime */,
false /* recordGCEndTime */,
false /* countCollection */ );
break;
case CMSCollector::FinalMarking:
initialize(true /* fullGC */ ,
cause /* cause of the GC */,
false /* recordGCBeginTime */,
false /* recordPreGCUsage */,
false /* recordPeakUsage */,
false /* recordPostGCusage */,
true /* recordAccumulatedGCTime */,
false /* recordGCEndTime */,
false /* countCollection */ );
break;
case CMSCollector::Sweeping:
initialize(true /* fullGC */ ,
cause /* cause of the GC */,
false /* recordGCBeginTime */,
false /* recordPreGCUsage */,
true /* recordPeakUsage */,
true /* recordPostGCusage */,
false /* recordAccumulatedGCTime */,
true /* recordGCEndTime */,
true /* countCollection */ );
break;
default:
ShouldNotReachHere();
}
}
cms就比較奇特了,他的+1條件有3種,InitialMarking,FinalMarking,Sweeping。
foreground
void CMSCollector::collect_in_foreground(bool clear_all_soft_refs, GCCause::Cause cause) {
checkpointRootsInitial(false);
...
checkpointRootsFinal(false, clear_all_soft_refs,
init_mark_was_synchronous);
···
sweep(false);
···
}
在foreground模式下,3中情況都有調用,也是就說一次完整的foreground會造成垃圾器次數+3。不過這種情況觸發條件還是比較苛刻的。
background
void CMSCollector::collect_in_background(bool clear_all_soft_refs, GCCause::Cause cause) {
...
sweep(true);
...
}
background模式下只有一處調用。也就是說正常的cms回收,老年代垃圾回收器是+1的。
G1
bool G1CollectedHeap::do_collection(bool explicit_gc,
bool clear_all_soft_refs,
size_t word_size) {
...
TraceMemoryManagerStats tms(true /* Full GC */,gc_cause);
...
}
G1的fullgc的時候老年代回收器會+1。這麼沒有mixed的事情,也就是說,只要參數設置合理,G1基本上是看不到老年代回收次數的。
總結
以上展示了各種老年代垃圾回收器jmx回收次數的採集。我們可以發現除了cms外,老年代垃圾回收的採集次數就是fullgc的次數。cms的老年代垃圾回收器的次數和jstat的fgc的值也是對不上的。需要根據gc日誌來進行分析,不要單純的看回收次數。