相关文章:
一、方法区 (Method Area)
-
方法区 (Method Area) 与 Java 堆一样,是各个线程共享的内存区域,用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据
-
当方法区无法满足内存分配的需求时,将会抛出 OutOfMemoryError 异常
二、永久代与元空间
-
在 Java 虚拟机规范中,只规定了方法区的概念及其作用,但并没有规定如何去实现它,这里我们来看看方法区的两个具体实现:永久代与元空间
-
两者最大的区别是:元空间使用的是本地内存,而永久代使用的是虚拟机的内存
-
永久代 (PermGen)
-
在 Java8 之前,永久代是方法区的具体实现 (仅针对 HotSpot 虚拟机,对于其他虚拟机如:BEA JRockit、IBM J9 等来说是不存在永久代的)
-
使用永久代来实现方法区,容易遇到内存溢出的问题,我们可以通过 ‑XX:MaxPermSize 参数来设置永久代的大小,一旦类的元数据超过了设定的大小,就会抛出 java.lang.OutOfMemoryError: PermGen space 异常,而且有极少数的方法 (例如:String.intern() 方法) 会因为这个原因导致在不同的虚拟机下有不同的表现
-
从 Java7 开始,原先位于方法区中的字符串常量池已被移动到 Java 堆中
-
-
元空间 (Metaspace)
-
从 Java8 开始,Java 虚拟机移除了永久代,转而使用元空间来代替它,与永久代不同的是,元空间使用本地内存来存放类的元数据而不是使用虚拟机的内存
为什么废除永久代 --> 官方说明
-
使用本地内存后,最直接的表现在于,原先在永久代中会出现的异常 - java.lang.OutOfMemoryError: PermGen space 将不复存在,因为默认类的元数据分配只受本地内存大小的限制,理论上本地内存有多少,元空间就可以有多大,这解决了内存空间不足的问题
-
虚拟机会在运行时会根据类的元数据大小动态地设置元空间的大小
-
-
三、元空间的内存管理
-
元空间的内存管理由元空间虚拟机来完成,先前使用永久代时,对于类的元数据我们需要用不同的垃圾回收器来进行处理,现在只需要执行元空间虚拟机的 C++ 代码即可完成
-
在元空间中,类和其元数据的生命周期和对应的类加载器是相同的,话句话说,只要类加载器存活,其加载的类的元数据也是存活的,因而不会被回收掉
-
每一个类加载器的存储区域都称作一个元空间,所有的元空间合在一起就是我们一直说的元空间。当一个类加载器被垃圾回收器标记为不再存活时,其对应的元空间就会被回收,在回收过程中没有重定位和压缩等操作,但是元空间内的元数据会进行扫描来确定 Java 引用
-
元空间虚拟机负责元空间的分配,其采用的形式为组块分配,组块的大小因类加载器的类型而异。在元空间虚拟机中存在一个全局的空闲组块列表,当一个类加载器需要组块时,它就会从这个全局的组块列表中获取并维持一个自己的组块列表。当一个类加载器不再存活时,那么其持有的组块将会被释放,并返回给全局组块列表。类加载器持有的组块又会被分为多个块,每一个块存储一个单元的元信息,组块中的块是线性分配的 (指针碰撞分配形式)。组块分配至内存映射区域,这些全局的虚拟内存映射区域以链表的形式连接,一旦某个虚拟内存映射区域被清空,那么这部分的内存就会返回给操作系统
四、元空间相比永久代的优势
-
字符串常量池存在永久代中,容易出现性能问题和内存溢出
-
类和方法的信息大小难以确定,给永久代的大小指定带来困难 (通过 ‑XX:MaxPermSize 参数来指定),太小容易导致永久代内存溢出,太大容易导致老年代内存溢出
-
永久代会为 GC 带来不必要的复杂性,且回收效率偏低,因为对永久代进行调优是很困难的,永久代中的元数据可能会随着每一次 Full GC 发生而进行移动,HostSpot 虚拟机的每种类型的垃圾回收器都需要特殊处理永久代中的元数据,将元数据分离出来后,可以简化 Full GC 以及对以后的并发隔离类类元数据等方面进行优化
-
方便 HotSpot 与其他虚拟机 (如:JRockit) 进行集成
五、归纳总结
-
方法区
-
用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据
-
Java8 之前,方法区的具体实现为永久代;Java8 开始,方法区的具体实现为元空间
-
元空间使用的是本地内存,而永久代使用的是虚拟机的内存
-
-
永久代
-
使用虚拟机内存
-
难以指定大小,容易出现性能问题和内存溢出
-
太小容易导致永久代内存溢出
-
太大容易导致老年代内存溢出
-
-
从 Java7 开始,将原先位于方法区中的字符串常量池移动到了 Java 堆中
-
-
元空间
-
使用本地内存
-
虚拟机在运行时会根据类的元数据大小动态地设置元空间的大小
-