为了账号安全,请及时绑定邮箱和手机立即绑定

CompressedClassSpace和metaspace指标关系

标签:
Java

当我们打开jconsole查看数据的时候,会发现下面两处数据。

  1. memorypool里metaspace和compressed class space是并列的。
    图片描述
  2. 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为最大值。

小结

  1. jmx的metaspace的使用值是包括了CompressedClassSpace的。
  2. CompressedClassSpace的最大值是MaxMetaspaceSize,InitialBootClassLoaderMetaspaceSize,CompressedClassSpaceSize这三个参数共同影响的结果。
  3. MaxMetaspaceSize是唯一最大限制,不会受到MetaspaceSize的干扰。
点击查看更多内容
1人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
JAVA开发工程师
手记
粉丝
1.6万
获赞与收藏
380

关注作者,订阅最新文章

阅读免费教程

感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消