当我们打开jconsole查看数据的时候,会发现下面两处数据。
- memorypool里metaspace和compressed class space是并列的。
- noheap里也专门分了两个柱状图。
这里就有一个疑问了CompressedClassSpace是不是和metaspace的指标是包含关系?
找出指标暴露类
我们通过mbean的类信息查找到对应数据读取。下面是获取使用的代码。
JNIEXPORT jobject JNICALL
Java_sun_management_MemoryPoolImpl_getUsage0
(JNIEnv *env, jobject pool)
{
jobject usage = jmm_interface->GetMemoryPoolUsage(env, pool);
if (usage == NULL) {
// Throw internal error since this implementation expects the
// pool will never become invalid.
JNU_ThrowInternalError(env, "Memory Pool not found");
}
return usage;
}
可以看出具体的指标获取在GetMemoryPoolUsage中实现。通过这个方法点击进入只能看到接口,并没有实现。这里的GetMemoryPoolUsage是通过宏定义实现的,需要全文检索来找到实现的位置。
// Returns a java/lang/management/MemoryUsage object containing the memory usage
// of a given memory pool.
JVM_ENTRY(jobject, jmm_GetMemoryPoolUsage(JNIEnv* env, jobject obj))
ResourceMark rm(THREAD);
MemoryPool* pool = get_memory_pool_from_jobject(obj, CHECK_NULL);
if (pool != NULL) {
MemoryUsage usage = pool->get_memory_usage();
Handle h = MemoryService::create_MemoryUsage_obj(usage, CHECK_NULL);
return JNIHandles::make_local(env, h());
} else {
return NULL;
}
JVM_END
在get_memory_pool_from_jobject中是可以根据我们的obj来获取到对应的MemoryPool。再往下就会追踪到如下的代码(中间省略其余的方法调用过程)。
MemoryPool* MemoryService::get_memory_pool(instanceHandle ph) {
for (int i = 0; i < _pools_list->length(); i++) {
MemoryPool* pool = _pools_list->at(i);
if (pool->is_pool(ph)) {
return pool;
}
}
return NULL;
}
我们可以看到,获取的操作就在_pools_list中遍历比较获得。那我们根据_pools_list来找加入的地方就可以找到数据来源了。
void MemoryService::add_metaspace_memory_pools() {
MemoryManager* mgr = MemoryManager::get_metaspace_memory_manager();
_metaspace_pool = new MetaspacePool();
mgr->add_pool(_metaspace_pool);
_pools_list->append(_metaspace_pool);
if (UseCompressedClassPointers) {
_compressed_class_pool = new CompressedKlassSpacePool();
mgr->add_pool(_compressed_class_pool);
_pools_list->append(_compressed_class_pool);
}
_managers_list->append(mgr);
}
metaspace的加入方法中会根据是否开启了指针压缩来增加_compressed_class_pool。
查看指标的数据来源
接下来我们来看看这两个pool的数据获取过程。
已使用指标
先看_compressed_class_pool,他的used的数据是通过获取Metaspace中的class信息的数据。
size_t CompressedKlassSpacePool::used_in_bytes() {
return MetaspaceUtils::used_bytes(Metaspace::ClassType);
}
static size_t used_bytes(Metaspace::MetadataType mdtype) {
return used_words(mdtype) * BytesPerWord;
}
我们再看MetaspacePool的获取。他获取是NonClassType和ClassType的总和。
size_t MetaspacePool::used_in_bytes() {
return MetaspaceUtils::used_bytes();
}
static size_t used_bytes() {
return used_words() * BytesPerWord;
}
static size_t used_words() {
return used_words(Metaspace::NonClassType) +
used_words(Metaspace::ClassType);
}
所以在使用量的指标上,Metaspace中是包含了compressed class的部分的。
最大值指标
最大值的指标是通过指标类的构造参数传入的
CompressedKlassSpacePool::CompressedKlassSpacePool() :
MemoryPool("Compressed Class Space", NonHeap, 0, CompressedClassSpaceSize, true, false) { }
compressed class是取的CompressedClassSpaceSize的值。这个值可以通过-XX:CompressedClassSpaceSize控制。但同时也受到其他参数影响。
size_t min_metaspace_sz =
VIRTUALSPACEMULTIPLIER * InitialBootClassLoaderMetaspaceSize;
if (UseCompressedClassPointers) {
if ((min_metaspace_sz + CompressedClassSpaceSize) > MaxMetaspaceSize) {
if (min_metaspace_sz >= MaxMetaspaceSize) {
vm_exit_during_initialization("MaxMetaspaceSize is too small.");
} else {
FLAG_SET_ERGO(size_t, CompressedClassSpaceSize,
MaxMetaspaceSize - min_metaspace_sz);
}
}
} else if (min_metaspace_sz >= MaxMetaspaceSize) {
FLAG_SET_ERGO(size_t, InitialBootClassLoaderMetaspaceSize,
min_metaspace_sz);
}
通过上面的代码,我们可以看到当min_metaspace_sz 加CompressedClassSpaceSize大于 MaxMetaspaceSize的时候,CompressedClassSpaceSize就强制被设置为(MaxMetaspaceSize - min_metaspace_sz)。
min_metaspace_sz是VIRTUALSPACEMULTIPLIER * InitialBootClassLoaderMetaspaceSize的乘积。VIRTUALSPACEMULTIPLIER是个宏是2,InitialBootClassLoaderMetaspaceSize是根据-XX:InitialBootClassLoaderMetaspaceSize的参数设置的。
CompressedClassSpaceSize是MaxMetaspaceSize,InitialBootClassLoaderMetaspaceSize,CompressedClassSpaceSize这三个参数共同影响的结果。
metaspace的最大值就比较简单。
MetaspacePool::MetaspacePool() :
MemoryPool("Metaspace", NonHeap, 0, calculate_max_size(), true, false) { }
size_t MetaspacePool::calculate_max_size() const {
return FLAG_IS_CMDLINE(MaxMetaspaceSize) ? MaxMetaspaceSize :
MemoryUsage::undefined_size();
}
static size_t undefined_size() { return (size_t) -1; }
最大值就是获取的MaxMetaspaceSize的值,如果这个值没有设置,那就是-1。这里也有一个注意的点。
if (MetaspaceSize > MaxMetaspaceSize) {
MetaspaceSize = MaxMetaspaceSize;
}
当MetaspaceSize的值超过MaxMetaspaceSize的时候,会拿MaxMetaspaceSize的赋值给MetaspaceSize。启动不会报错,所以一直是以MaxMetaspaceSize为最大值。
小结
- jmx的metaspace的使用值是包括了CompressedClassSpace的。
- CompressedClassSpace的最大值是MaxMetaspaceSize,InitialBootClassLoaderMetaspaceSize,CompressedClassSpaceSize这三个参数共同影响的结果。
- MaxMetaspaceSize是唯一最大限制,不会受到MetaspaceSize的干扰。
共同学习,写下你的评论
评论加载中...
作者其他优质文章