G1调优实践日记--被误解的MetaspaceSize

Metaspace

Metaspace 大家应该很熟悉了,所有线程共享的一块内存区域,主要存放已被虚拟机加载的类定义,方法定义,常量等一些元数据信息,运行时常量池(Runtime Constant Pool)也是方法区的一部分,Class 文件中的常量池表(Constant Pool Table,里面各种字面量和符号引用),被类加载后就放入方法区的运行时常量池 。运行时常量池并不只有编译时才往里放东西,运行时也可以放新的常量,比如 String.intern()。有一个别名叫“非堆”。

jdk1.8以后直接用本地内存实现方法区,并改名叫 Metaspace。正因为用本地内存(native memory),所以它的最大内存可以达到机器内存的极限,但关于它的调优参数一直有个误解。

MetaspaceSize

-XX:MetaspaceSize 并不代表初始的 Metaspace 大小,在 oracle doc 中,有明确的解释

Class metadata is deallocated when the corresponding Java class is unloaded. 
Java classes are unloaded as a result of garbage collection, and garbage collections may be induced to unload classes and deallocate class metadata. 
When the space committed for class metadata reaches a certain level (a high-water mark), a garbage collection is induced. 
After the garbage collection, the high-water mark may be raised or lowered depending on the amount of space freed from class metadata. 
The high-water mark would be raised so as not to induce another garbage collection too soon. 
The high-water mark is initially set to the value of the command-line option -XX:MetaspaceSize.
It is raised or lowered based on the options -XX:MaxMetaspaceFreeRatio and -XX:MinMetaspaceFreeRatio. 
If the committed space available for class metadata as a percentage of the total committed space for class metadata is greater than -XX:MaxMetaspaceFreeRatio, then the high-water mark will be lowered. 
If it's less than -XX:MinMetaspaceFreeRatio, then the high-water mark will be raised.

大致意思就是当 MetaspaceSize 接近一个指定水位(high-water mark)的时候,会引发垃圾回收,这个初始化的水位值就是 -XX:MetaspaceSize

另一方面,水位值会上下浮动,上浮主要是为了避免过早引发一次垃圾回收,而上下浮动主要由 两个参数控制

  • -XX:MaxMetaspaceFreeRatio:已提交的 Metaspace 空间中剩余可用的占全部提交空间的比例 大于 MaxMetaspaceFreeRatio ,说明 Metaspace 空间仍有富余,会降低水位。
  • -XX:MinMetaspaceFreeRatio:反之,如果比例小于 MinMetaspaceFreeRatio ,说明 Metaspace 空间比较紧张,会升高水位。

这说明如果你只指定 -XX:MetaspaceSize,然后再做 Metaspace oom实验,并不代表 gc 会在你指定的值发生,因为这个值会上下浮动。

最后再提下 -XX:MaxMetaspaceSize 就是字面意思,会给 MetaspaceSize 设一个上限,默认空间是无限的(default unlimited)。

-XX:MetaspaceSize 验证实验

前面说了,要做 Metaspace oom 实验还需要考虑 MaxMetaspaceFreeRatioMinMetaspaceFreeRatio 的设置,这次我的 gc 参数设为如下,都设为 0 是避免水位上下浮动:

-XX:+UseG1GC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:MetaspaceSize=8m -XX:MaxMetaspaceFreeRatio=0 -XX:MinMetaspaceFreeRatio=0

然后自定义一个大对象 OomObject

public class OomObject {

    String[] strings;

    public OomObject() {
        this(1);
    }

    /**
     * size,unit mb
     */
    public OomObject(int size) {
        if (size <= 0) {
            size = 1;
        }
        strings = new String[size];
        for (int i = 0; i < size; i++) {
            strings[i] = new String(createObject());
        }
    }

    /**
     * create 1mb object
     *
     * @return
     */
    private byte[] createObject() {
        return new byte[1024 * 300];
    }
}

然后单元测试 会无限循环创建 OomObject 的子类(通过cglib,并且关闭缓存),这样会慢慢填充 MetaspaceSize,并且实时打印 MetaspaceSize log。

    @Test
    public void testMetaSpaceOom() throws InterruptedException {
//        ArrayList<Object> objects = new ArrayList<Object>();
        List<MemoryPoolMXBean> memoryPoolMXBeans = ManagementFactory.getMemoryPoolMXBeans();

        while (true) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(OomObject.class);
            enhancer.setUseCache(false);
            enhancer.setCallback(new MethodInterceptor() {
                @Override
                public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
                        throws Throwable {
                    return proxy.invokeSuper(obj, args);
                }
            });
            enhancer.create();
            Thread.sleep(200);
            for (MemoryPoolMXBean poolMXBean : memoryPoolMXBeans) {
                if (MemoryType.NON_HEAP.equals(poolMXBean.getType())) {
                    MemoryUsage usage = poolMXBean.getUsage();
                    if (poolMXBean.getName().contains("Metaspace")) {
                        System.out.println(poolMXBean.getName() + ":" + usage.getCommitted());
                    }
                }
            }
        }
    }

最后我们可以看到成果,在 MetaspaceSize 达到 8m 的时候,会触发 Metadata GC Threshold gc,验证了 -XX:MetaspaceSize 的作用。

···
Metaspace:8388608
Metaspace:8388608
2021-08-26T15:59:08.109+0800: [GC pause (Metadata GC Threshold) (young) (initial-mark), 0.0046983 secs]
   [Parallel Time: 3.6 ms, GC Workers: 8]
      [GC Worker Start (ms): Min: 36019.2, Avg: 36019.4, Max: 36020.0, Diff: 0.8]
      [Ext Root Scanning (ms): Min: 0.0, Avg: 0.5, Max: 0.8, Diff: 0.8, Sum: 4.0]
 ···
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值