Metaspace 的使用量与JVM加载到内存中的 class 数量/大小有关。可以说, java.lang.OutOfMemoryError: Metaspace\ 错误的主要原因, 是加载到内存中的 class 数量太多或者体积太大。
示例
和 PermGen 类似, Metaspace 空间的使用量, 与JVM加载的 class 数量有很大关系。下面是一个简单的示例:
1 2 3 4 5 6 7 8 9 10
public class Metaspace { static javassist.ClassPool cp = javassist.ClassPool.getDefault();
public static void main(String[] args) throws Exception{ for (int i = 0; ; i++) { Class c = cp.makeClass("eu.plumbr.demo.Generated" + i).toClass(); } } } 12345678910
可以看到, 使用 javassist 工具库生成 class 那是非常简单。在 for 循环中, 动态生成很多class, 最终将这些class加载到 Metaspace 中。
java.lang.OutOfMemoryError: PermGen space 错误信息所表达的意思是: 永久代(Permanent Generation) 内存区域已满
原因分析
我们先看看 PermGen 是用来干什么的。
在JDK1.7及之前的版本, 永久代(permanent generation) 主要用于存储加载/缓存到内存中的 class 定义, 包括 class 的 名称(name), 字段(fields), 方法(methods)和字节码(method bytecode); 以及常量池(constant pool information); 对象数组(object arrays)/类型数组(type arrays)所关联的 class, 还有 JIT 编译器优化后的class信息等。
很容易看出, PermGen 的使用量和JVM加载到内存中的 class 数量/大小有关。可以说 java.lang.OutOfMemoryError: PermGen space 的主要原因, 是加载到内存中的 class 数量太多或体积太大。
示例
最简单的例子
我们知道, PermGen 空间的使用量, 与JVM加载的 class 数量有很大关系。下面的代码演示了这种情况:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
import javassist.ClassPool;
public class MicroGenerator { public static void main(String[] args) throws Exception { for (int i = 0; i < 100_000_000; i++) { generate("eu.plumbr.demo.Generated" + i); } }
public static Class generate(String name) throws Exception { ClassPool pool = ClassPool.getDefault(); return pool.makeClass(name).toClass(); } }
这段代码在 for 循环中, 动态生成了很多class。可以看到, 使用 javassist 工具类生成 class 是非常简单的。
执行这段代码, 会生成很多新的 class 并将其加载到内存中, 随着生成的class越来越多,将会占满Permgen空间, 然后抛出 java.lang.OutOfMemoryError: Permgen space 错误, 当然, 也有可能会抛出其他类型的 OutOfMemoryError。
package com.cncounter.rtime; import java.util.Map; import java.util.Random; public class TestWrapper { public static void main(String args[]) throws Exception { Map map = System.getProperties(); Random r = new Random(); while (true) { map.put(r.nextInt(), "value"); } } }
配置JVM参数: -Xmx12m。执行时产生的错误信息如下所示:
1 2 3 4
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded at java.util.Hashtable.addEntry(Hashtable.java:435) at java.util.Hashtable.put(Hashtable.java:476) at com.cncounter.rtime.TestWrapper.main(TestWrapper.java:11)
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at java.util.Hashtable.rehash(Hashtable.java:401) at java.util.Hashtable.addEntry(Hashtable.java:425) at java.util.Hashtable.put(Hashtable.java:476) at com.cncounter.rtime.TestWrapper.main(TestWrapper.java:11)
@Override public int hashCode() { return id.hashCode(); } }
public static void main(String[] args) { Map m = new HashMap(); while (true){ for (int i = 0; i < 10000; i++){ if (!m.containsKey(new Key(i))){ m.put(new Key(i), "Number:" + i); } } System.out.println("m.size()=" + m.size()); } } }
工作,其实是个人与企业做的一次长达 N 年的交易,这 N 年期间,企业为个人提供薪资,个人为企业提供产品(代码、服务等等)。个人随时可能出现不合规范的交易(比如交不出好的产品,或者突然提离职),企业也随时可能出现不合规范的交易(比如单方面解除合同,延期发工资等),双方都有能力对对方造成损失。企业为此做了充足的准备,比如一些关键岗位必须设置 backup,防止关键人物突然离职造成的业务停滞,将意外造成的损失最小化;而个人往往没有对此做足够的准备,一方面可能被企业宣导的「大家庭」理念误导了。所以,认清「人与企业之间是单纯的利益关系,而不是亲戚朋友的关系」这个事实,保持好心态,与企业公平交易即可。