方法区和永久代区别

区别

    永久代(PerGen space)是对方法区的一种实现,类似实现类和接口的关系。可以说,方法区是JVM的一种规范,而永久代是对这一种规范的实现。另外只有hotspot虚拟机才有永久代的概念,而其他虚拟机,如JRockit(Oracle)、J9(IBM),并没有永久代的概念。 由于方法区主要存储类的相关信息,所以对于动态生成类的情况比较容易造成永久代的内存溢出。比较典型的场景就是,在jsp页面比较多的情况下,容易出现永久代内存溢出(java.lang.OutOfMemoryError: PermGen)。

    在jdk7中已经将字符串常量池从方法区移除,并在Java堆中开辟一块新的区域存放字符串常量池,譬如符号引用(symbols)转移到了native heap;字面量(interned strings)转移到了java heap;类的静态变量(class statics)转移到了java heap。而在jdk8中,已经没有了永久代,取而代之的是叫做元空间(metaspace)的内存区域。

    元空间与永久代最大的区别就是元空间的内存区域并不在虚拟机中,而是使用本地内存。因此,默认情况下元空间的大小仅受本地内存限制。
移除永久代后,不会遇到永久代存在的内存溢出错误,也不会出现泄漏的数据移到交换区这样的事情。最终用户可以为元空间设置一个可用空间最大值,如果不进行设置,JVM会自动根据类的元数据大小动态增加元空间的容量。

题外话

    Heap Memory是供Java应用程序使用的。Native Memory没有相应的参数来控制大小,其大小依赖于操作系统进程的最大值,以及生成的Java字节码大小、创建的线程数量、维持java对象的状态信息大小(用于GC)以及一些第三方的包,比如JDBC驱动使用的native内存。

Native Memory

  • 管理java heap的状态数据(用于GC);
  • JNI调用,也就是Native Stack;
  • JIT(即使编译器)编译时使用Native Memory,并且JIT的输入(Java字节码)和输出(可执行代码)也都是保存在Native Memory;
  • NIO direct buffer。对于IBM JVM和Hotspot,都可以通过-XX:MaxDirectMemorySize来设置nio直接缓冲区的最大值。默认是64M。超过这个时,会按照32M自动增大。

DirectBuffer

    DirectBuffer访问更快,避免了从HeapBuffer还需要从java堆拷贝到本地堆,操作系统直接访问的是DirectBuffer。DirectBuffer对象的数据实际是保存在native heap中,但是引用保存在HeapBuffer中。另外,DirectBuffer的引用是直接分配在堆得Old区的,因此其回收时机是在FullGC时。因此,需要避免频繁的分配DirectBuffer,这样很容易导致Native Memory溢出。

参考资料

java 8中撤销永久代,引入元空间
JVM的Heap Memory和Native Memory