原空间(Meta Space)
? 用于存放类信息、常量、静态变量、JIT即时编译器百年以后的机器代码等数据等。例如:java.lang.Object类的元信息、Integer.MAX_VALUE等常量。
JDK1.6
HotSpot JVM 使用Method Area方法区存储,也叫永久代(Permanent Generation)。
- 方法区和“永久代(Permanent Generation)”的区别:方法区是JVM的规范,而永久代(Permanent Generation)是JVM规范的一种实现,并且只有HotSpot JVM才有永久代“Permanent Generation”,而对于其他类型的虚拟机。如JRockit(Oracle)、J9(IBM)并没有。
- 方法区是一片连续的堆空间,当JVM加载的类的信息容量超过了最大可分配空间,虚拟机会抛出OutOfMemoryError : PermGenspace 的Error 。
- 永久代的GC是和老年代( old generation )捆绑在一起的,无论谁满了,都会触发永久代和老年代的垃圾收集。
- 可以通过-XX:PermSize=N设置方法区(永久代)初始空间,-XX:MaxPermSize=N设置方法区(永久代)最大空间,超过这个值将会抛出错误:java.lang.outOfMemoryError: PermGen
JDK1.7
将字符串常量池、静态变量转移到了堆区。
JDK1.8:正式移除永久代,采用Meta Space元空间代替
? 元空间的本质和永久代类似,都是对JVM规范中方法区的一种具体实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制,但可以通过运行参数来指定元空间的大小。
? Java 8 中PermGen永久代为什么被移出HotSpot JVM ?
? ·由于PermGen内存经常会溢出,容易抛出java.lang.outOfMemoryError: PermGen错误;
? ·移除PermGen 可以促进HotSpot JVM与JRockit VM的融合,因为JRockit没有永久代;
示例1:不断的生成新的字符串,快速的消耗内存。通过JDK1.6、JDK1.7和JDK1.8分别运行。
public class TestOOM {
static String base = "ApeSource";
public static vois main(String[] args){
List<String> list = new ArrayLiat<String>();
for(int i = 0;i < Integer.MAX.VALUE;i++){
String str = base + base;
base = str;
list.add(str.intern());
}
}
}
JDK 1.6
JDK 1.7
JDK 1.8
? 上述运行结果可以看出,相同的代码,在JDK 1.6会出现“PermGen Space”的永久代内存溢出,而在JDK 1.7和JDK 1.8中,会出现"Java heap space"堆内存溢出,并且DK 1.8中 PermSize和MaxPermGen参数已经无效。因此,在JDK 1.7和JDK 1.8中,已经将字符串常量由永久代转移到堆中,并且JDK 1.8中已经完全移除了永久代,采用元空间来代替。
示例2:在JDK 8下重新运行一下运行测试代码TestoOM,指定MetaSpacesize和MaxMetaSpacesize的大小,输出结果如下:
- -XX:MetaspaceSize参数:主要控制Meta Space GC发生的初始阈值,也就是最小阈值,当使用的Meta Space空间到达MetaspaceSize的时候,就会触发Metaspace的GC。
- XX:MaxMetaspaceSize参数:最大空间,默认是没有限制的。在jvm启动的时候,并不会分配 MaxMetaspaceSize这么大的一块内存出来,metaspace是可以一直扩容的,直到到达MaxMetaspacesize。