分类: Java开发

关于 MetaSpace 及 FastJSON 导致的 OOM

关于 MetaSpace 内存

在 JDK8 之前,虚拟机内存主要分为堆和非堆两部分,堆中划分新生代老生代,非堆中包含所有非对象信息和运行时信息,其中有一块叫 PermGen,用以保存字节码等类信息。在 JDK8 中取消了这块内存,并添加了 MetaSpace 替代所有功能。

这两块的区别

最大的区别就是位置不同大小不同,PermGen 仍然归属 JVM 内存,一般 32M 或者 64M,都很小,MetaSpace 直接位于本地内存,默认大小只受物理机限制,直到用完物理机内存才抛出 OOM。

所以在某些情况下直接升级 JDK8 可能就出现内存持续增长的情况,在这种情况下通过 top 命令会发现内存猛涨,远超 Xmx 设置的大小, 但通过 jmap 则发现正常。

例如

先看段 fastjson 的代码:

public void process(HttpServletResponse resp) {
    ....
    SerializeConfig config = new SerializeConfig();
    config.put(Long.class, RsLongSerializer.instance);
    resp.getWriter().print(JSON.toJSONString(obj,config));
}

这段代码的问题就在 SerializeConfig 默认会激活 asm,在序列化对象时会为对象生成代理类,然后通过执行代理进行序列化操作,通过这样优化来提高执行性能,但在应用不合理每次新创建 config 的时候就会导致大量生成代码类反而拖慢性能。反序列化时的 ParserConfig 也是同理。

在 jdk8 之前这些代理类会充满 Perm 区导致 FullGC,浪费点 CPU 也不会有大问题,但在 JDK8 中,这些类会大量创建直至充满物理机内存,进而导致进程被系统杀掉。

附 MetaSpace 相关参数

  • -XX:MetaspaceSize,初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值。
  • -XX:MaxMetaspaceSize,最大空间,默认是没有限制的。
  • -XX:MinMetaspaceFreeRatio,在GC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾收集
  • -XX:MaxMetaspaceFreeRatio,在GC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集

All Done!

在系统升级的时候还是要谨慎点,一定要做好预研,尽量避免这种由于机制变化导致的系统问题。

Recent Posts

Docker 容器非 root 用户监听 80 端口

起因是基于 CentOS 的 …

1 年 之前

基于 Docker 定时打印文件

先说背景,喷墨打印机有个很大的…

2 年 之前

Java 运行时反射获取来自继承的泛型

背景 正常情况下 Java 的…

2 年 之前

Java 基于 ByteBuddy 重写系统当前时间

背景 一般单元测试时总会有些代…

2 年 之前

华硕 B450F-Gaming 主板 I211-AT 网卡驱动安装

事情起因是买了块华硕的 ROG…

3 年 之前

PHP 安装 Memcached 扩展

登录服务器挨步执行: # su…

3 年 之前