jdk 1.7 之前 jvm 使用永久代 (PermGen) 来管理方法区, 而 jdk 1.8 之后 jvm 使用元数据区 (Metaspace) 来管理方法区, 实现了革命性得性能提升;
本文来简要梳理一下 PermGen 和 Metaspace 的区别和性能差异点;
Metaspace 与 PermGen 的对比
特性 | PermGen (≤ Java 7) | Metaspace (≥Java 8) |
---|---|---|
存储位置 | 堆内存的一部分 (固定大小) | 本地内存 |
内存管理 | 由 JVM 管理,受 -XX:MaxPermSize 限制 | 由操作系统管理,默认无上限(受 -XX:MaxMetaspaceSize 约束) |
static 变量处理 | 存储在 PermGen 中, 增加了方法区大小 | 存储在堆中, 彻底隔离方法区和运行时数据, 提升 Metaspace 的纯度 |
垃圾回收 | 由 Full GC 触发 | 由专门的 Metaspace GC (如 ClassUnloading) 回收 |
OOM 风险 | 会发生 OutOfMemoryError: PermGen space | 可能发生 OutOfMemoryError: Metaspace |
Metaspace 的关键参数:
1 | -XX:MetaspaceSize=256M # 初始大小 |
Metaspace 替代 PermGen 的原因
提升类卸载效率
- PermGen 的类卸载只能依赖 Full GC, 回收不及时, 可能造成实质上的泄漏, 效率低;
- Metaspace 由专门的 类卸载机制 管理, 可与 java 堆共享垃圾收集器, 当类加载器被回收时, 其加载的类元数据也会被自动回收, GC 更高效;
适应动态语言 (如 Groovy / Scala)
- java 8 引入 Lambda 和方法引用, 需要更灵活的元数据存储;
无默认大小限制
- PermGen 大小默认有限制而且限制很严格 (默认 64 MB ~ 82 MB)
- 该阈值很低因为其占用了堆内存, 分配过大会影响其他对象在堆上的分配, 易导致 OutOfMemoryError (尤其在动态加载大量类时, 如 spring / cglib);
- Metaspace 使用本地内存, 不占用宝贵的堆内存, 且分配效率高, 故允许默认无上限, 可按需分配 (当然生产环境建议依然要设置 MaxMetaspaceSize);
- 该阈值很低因为其占用了堆内存, 分配过大会影响其他对象在堆上的分配, 易导致 OutOfMemoryError (尤其在动态加载大量类时, 如 spring / cglib);
- PermGen 大小默认有限制而且限制很严格 (默认 64 MB ~ 82 MB)