jmx会暴露进程开始运行后的gc总次数和gc总时间。这个数据究竟是怎么获取的。下面以ps为例。我们来查看一下流程。
for (GarbageCollectorMXBean garbageCollectorMBean : garbageCollectors) {
String gcName = garbageCollectorMBean.getName();
long gcCount = garbageCollectorMBean.getCollectionCount();
}
java层面的api就是这一个样子,可以从mbean众读取gc的次数。
我们从接口找到实现类。发现这是一个native的方法。
public class GarbageCollectorImpl extends MemoryManagerImpl
implements GarbageCollectorMXBean {
protected GarbageCollectorImpl(String name) {
super(name);
}
@Override
public native long getCollectionCount();
根据jni的规则。我们找到具体的c实现。
JNIEXPORT jlong JNICALL Java_sun_management_GarbageCollectorImpl_getCollectionCount
(JNIEnv *env, jobject mgr) {
return jmm_interface->GetLongAttribute(env, mgr, JMM_GC_COUNT);
}
这里的JMM_GC_COUNT是个枚举。
static jlong get_gc_attribute(GCMemoryManager* mgr, jmmLongAttribute att) {
switch (att) {
case JMM_GC_TIME_MS:
return mgr->gc_time_ms();
case JMM_GC_COUNT:
return mgr->gc_count();
case JMM_GC_EXT_ATTRIBUTE_INFO_SIZE:
// current implementation only has 1 ext attribute
return 1;
default:
assert(0, "Unrecognized GC attribute");
return -1;
}
}
size_t gc_count() { return _num_collections; }
在实现的代码里,我们可以看到数据是直接从mgr的gc_count中获取的。gc_count是直接获取的一个成员变量_num_collections。
void GCMemoryManager::gc_end(bool recordPostGCUsage,
bool recordAccumulatedGCTime,
bool recordGCEndTime, bool countCollection,
GCCause::Cause cause,
bool allMemoryPoolsAffected) {
…………
_num_collections++;
GCMemoryManager的gc_end。在MemoryService中被调用到。
void MemoryService::gc_end(GCMemoryManager* manager, bool recordPostGCUsage,
bool recordAccumulatedGCTime,
bool recordGCEndTime, bool countCollection,
GCCause::Cause cause,
bool allMemoryPoolsAffected) {
manager->gc_end(recordPostGCUsage, recordAccumulatedGCTime, recordGCEndTime,
countCollection, cause, allMemoryPoolsAffected);
}
MemoryService在TraceMemoryManagerStats的析构函数里被调用。
TraceMemoryManagerStats::~TraceMemoryManagerStats() {
MemoryService::gc_end(_gc_memory_manager, _recordPostGCUsage, _recordAccumulatedGCTime,
_recordGCEndTime, _countCollection, _cause, _allMemoryPoolsAffected);
}
jvm很多的计数方式都是通过析构函数做的。TraceMemoryManagerStats就是我们需要找的对象。
TraceMemoryManagerStats分散在每个gc的实现里。下面是PSParallelCompact的部分
TraceMemoryManagerStats tms(heap->old_gc_manager(), gc_cause);
这里的调用为了能准确的析构,专门把gc的代码放在了一个代码块里。通过声明周期的控制,达到跟踪数据的目的。
后面还有很多计数也在这里的code里。例如gclog
void PSOldGen::print_used_change(size_t prev_used) const {
log_info(gc, heap)("%s: " SIZE_FORMAT "K->" SIZE_FORMAT "K(" SIZE_FORMAT "K)",
name(), prev_used / K, used_in_bytes() / K, capacity_in_bytes() / K);
}
还有jstat的值的变化
void ParallelScavengeHeap::update_counters() {
young_gen()->update_counters();
old_gen()->update_counters();
MetaspaceCounters::update_performance_counters();
CompressedClassSpaceCounters::update_performance_counters();
}
nmt的变化
// Track memory usage and detect low memory
MemoryService::track_memory_usage();