方法区
方法区与 Java 堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息
、常量
、静态变量
、即时编译器编译后的代码
等数据。虽然 Java 虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做 Non-Heap(非堆)
,目的应该是与 Java 堆区分开来。
永久代(PermGen)
《Java 虚拟机规范》只是规定了有方法区这么个概念和它的作用,并没有规定如何去实现它。那么,在不同的 JVM 上方法区的实现肯定是不同的了。
方法区和永久代的关系很像 Java 中接口和类的关系,类实现了接口,而永久代就是 HotSpot 虚拟机对虚拟机规范中方法区的一种实现方式。也就是说,永久代是 HotSpot 的概念,方法区是 Java 虚拟机规范中的定义,是一种规范,而永久代是一种实现,一个是标准一个是实现,其他的虚拟机实现并没有永久代这一说法。
元空间(MetaSpace)
在 Java 官方的 HotSpot 虚拟机中,Java8 版本以后,是用元空间来实现的方法区;在Java8之前的版本,则是用永久代实现的方法区。
方法区大小常用参数
JDK 1.8 之前永久代还没被彻底移除的时候,通常通过下面这些参数来调节方法区大小:
-XX:PermSize=N
方法区 (永久代) 初始大小-XX:MaxPermSize=N
方法区 (永久代) 最大值
超过这个值将会抛出 OutOfMemoryError 异常,即 java.lang.OutOfMemoryError: PermGen
相对而言,垃圾收集行为是很少出现在这个区域的,但并非数据进入方法区后就「永久存在」了。
JDK 1.8 的时候,方法区(HotSpot 的永久代)被彻底移除了(JDK1.7 就已经开始了),取而代之是元空间
,元空间使用的是直接内存
。
-XX:MetaspaceSize=N
设置 Metaspace 的初始值(和最小大小),如果未指定此标志,则 Metaspace 将根据运行时的应用程序需求动态地重新调整大小。-XX:MaxMetaspaceSize=N
设置 Metaspace 的最大值,默认值为 unlimited,这意味着它只受系统内存的限制。
当元空间溢出时会得到如下错误: java.lang.OutOfMemoryError: MetaSpace
与永久代很大的不同就是,如果不指定大小的话,随着更多类的创建,虚拟机会耗尽所有可用的系统内存。
为什么将永久代替换为元空间?
永久代有一个 JVM 本身设置固定大小上限,无法进行调整,而元空间使用的是直接内存
,受本机可用内存的限制,虽然元空间仍旧可能溢出,但是比原来出现的几率会更小。
- 元空间里面存放的是
类的元数据
,这样加载多少类的元数据就不由MaxPermSize
控制了, 而由系统的实际可用空间来控制,这样能加载的类就更多。 - 在 JDK1.8,合并 HotSpot 和 JRockit 的代码时,JRockit 从来没有一个叫永久代的东西,合并之后就没有必要额外地设置这么一个永久代的地方了。