JVM永久代(PernGem)和元空间(Metaspace)
来源:互联网 发布:淘宝流量下降的原因 编辑:程序博客网 时间:2024/06/06 09:36
前言
在之前的文章中提到过方法区存放的是虚拟机已经加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。同时运行时常量池也是方法区的一部分。但是在不同的JDK版本中对方法区的实现方式存在一定的差异。
正题
下面通过对JDK1.6、JDK1.7、JDK1.8的运行时常量池的对比来看看各自的实现方式。
JDK1.6
在JDK1.6及之前的版本中(针对HotSpot虚拟机),由于常量池分配在永久代内。
public class RuntimeConstantPoolOOM {public static void main(String[] args) {List<String> list = new ArrayList<String>();int i = 0;while(true){System.out.println(i);list.add(String.valueOf(i++).intern());}}}JDK版本号:
java version "1.6.0_20"Java(TM) SE Runtime Environment (build 1.6.0_20-b02)Java HotSpot(TM) 64-Bit Server VM (build 16.3-b01, mixed mode)
运行结果:
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space at java.lang.String.intern(Native Method) at com.access.RuntimeConstantPoolOOM.main(RuntimeConstantPoolOOM.java:14)
从运行结果中可以看出,运行时常量池溢出,在OutOfMemoryError后面跟随的提示信息是“PermGen spacer”,说明运行时常量池属于方法区(HotSpot虚拟机中的永久代)的一部分。
通过使用Java VisualVM工具观察PermGen内存运行轨迹:
从上图可以看出:因为程序通过使用列表保存在PernGem区域中字符串的引用,导致PermGen区域的内存不断飙升直至抛出OutOfMemoryError异常。
如果不把String对象add到list列表的情况:
从上图可以看出:只要PermGen的使用量接近或达到最大大小时就会触发PermGen区域的垃圾回收。
JDK1.7
JDK版本号:
java version "1.7.0_79"Java(TM) SE Runtime Environment (build 1.7.0_79-b15)Java HotSpot(TM) 64-Bit Server VM (build 24.79-b02, mixed mode)
通过使用Java VisualVM工具观察PernGen内存运行轨迹图:
从PernGen运行轨迹看出:PernGen很平稳,说明常量池没有分配到PernGen内存区域中。
通过使用Java VisualVM工具观察堆内存运行轨迹图:
从堆内存运行轨迹可以看出:堆不不断增加,最后导致抛出OutOfMemoryError异常。从上图可以知道常量池是在堆上分配存储空间。
运行结果:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOf(Unknown Source) at java.util.Arrays.copyOf(Unknown Source) at java.util.ArrayList.grow(Unknown Source) at java.util.ArrayList.ensureExplicitCapacity(Unknown Source) at java.util.ArrayList.ensureCapacityInternal(Unknown Source) at java.util.ArrayList.add(Unknown Source) at com.access.RuntimeConstantPoolOOM.main(RuntimeConstantPoolOOM.java:14)
从运行结果可以看出,JDK1.7把常量池从永久代移动到Java堆中,所以在Exception中提示heap溢出。
JDK1.8
随着JDK8的到来,JVM不在有PermGen。但类的元数据信息(metadata)还在,只不过不在是存储在连续的堆空间上,而是移动到叫做“Metaspace”的本地内存(Native memory)中。
元空间的本质和永久代类似,都是对JVM规范中方法区的实现,不过元空间和永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。理论上取决于32位/64位系统可虚拟的内存大小。可见也不是无限的,需要配置参数。
现在大多数的类元数据分配在本地化内存中。默认情况下,类元数据分配受到可用的本机内存容量的限制(容量依然取决于你使用的是32位JVM还是64位操作系统的虚拟内存的可用性。在我的机子上的可用内存是1082130432B)。
类的元数据信息转移到Metaspace的原因:
字符串存放在永久代中,容易出现性能问题和内存溢出。
PermGen这个区域的回收“成绩”比较难以令人满意,尤其是类型的卸载,条件相当苛刻。
常用配置参数:
1:MetaspaceSize
初始化Metaspace大小,控制元空间发生GC的阈值。GC后,动态增加或降低MetaspaceSize。在默认情况下,这个值大小根据不同的品台在12M到20M浮动,使用Java -XX:+PrintFlagsInitial命令查看本机的初始化参数
2:MaxMetaspaceSize
限制Metaspace增长上限,防止因为某些情况导致Metaspace无限使用本地内存,影响到其他程序。
3:MinMetaspaceFreeRatio
当进行过Metaspace GC之后,会计算当前Metaspace的空闲空间比,如果空闲比小于这个参数(即实际非空闲空间占比过大,内存不够用),那么虚拟机将增加Metaspace的大小。默认值为40,也就是40%。设置该参数可以控制Metaspace的增长速度,太小的值会导致Metaspace增长缓慢,Metaspace的使用逐渐趋于饱和,可能会影响之后类的加载。而太大的值会导致Metaspace增长过快,浪费内存。
4:MaxMetaspaceFreeRatio
当进行Metaspace GC之后,会计算当前Metaspace的空闲空间比,如果空闲比大于这个参数,那么虚拟机就会释放Metaspace的部分参数。默认值为70,也就是70%。
5:MaxMetaspaceExpansion
Metaspace增长时的最大幅度。
6:MinMetaspaceExpansion
Metaspace增长时最小幅度。
在eclipse中选中类设置参数:-Xms20m -Xmx20 -XX:PermSize=8m -XX:MaxPermSize=8M
运行时会提示:
ClassA类
package com.memory;public class ClassA {}把ClassA编译成class文件,运行下面程序。
package com.memory;import java.io.File;import java.lang.management.ClassLoadingMXBean;import java.lang.management.ManagementFactory;import java.net.URL;import java.net.URLClassLoader;import java.util.ArrayList;import java.util.List;public class OOMTest {public static void main(String[] args) { try{ //准备url URL url = new File("D:/eclipse/javaEE/WebWorkSpace/ClassProject/src").toURI().toURL(); URL[] urls = {url}; //获取有关类型加载的JMX接口 ClassLoadingMXBean loadingBean = ManagementFactory.getClassLoadingMXBean(); //用于缓存类加载器 List<ClassLoader> classLoaders = new ArrayList<ClassLoader>(); while(true) { //加载类型并缓存类加载器实例 ClassLoader classLoader = new URLClassLoader(urls); classLoaders.add(classLoader); classLoader.loadClass("com.memory.ClassA"); //显示数量信息(共加载过的类型数目,当前还有效的类型数目,已经被卸载的类型数目) System.out.println("total: "+ loadingBean.getTotalLoadedClassCount()); System.out.println("active: "+ loadingBean.getLoadedClassCount()); System.out.println("unloaded: "+ loadingBean.getUnloadedClassCount()); } } catch(Exception e) { e.printStackTrace(); } } }运行时参数设置:-XX:MetaspaceSize=8m -XX:MaxMetaspaceSize=512m -XX:PermSize=8m -XX:MaxPermSize=8M
Metaspace空间的运行轨迹:
可以从上面的JVisualVM的截图看出:当加载类的总数到达87386之后,就会出现Metaspace区域溢出。也可以通过观察垃圾回收活动轨迹了解元空间内存耗尽。Java程序运行结果:
total: 87416active: 87416unloaded: 0Exception in thread "main" java.lang.OutOfMemoryError: Metaspaceat java.lang.ClassLoader.defineClass1(Native Method)at java.lang.ClassLoader.defineClass(Unknown Source)at java.security.SecureClassLoader.defineClass(Unknown Source)at java.net.URLClassLoader.defineClass(Unknown Source)at java.net.URLClassLoader.access$100(Unknown Source)at java.net.URLClassLoader$1.run(Unknown Source)at java.net.URLClassLoader$1.run(Unknown Source)at java.security.AccessController.doPrivileged(Native Method)at java.net.URLClassLoader.findClass(Unknown Source)at java.lang.ClassLoader.loadClass(Unknown Source)at java.lang.ClassLoader.loadClass(Unknown Source)at com.memory.OOMTest.main(OOMTest.java:26)Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=8m; support was removed in 8.0Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=8M; support was removed in 8.0
- JVM永久代(PernGem)和元空间(Metaspace)
- JVM在JDK8取消了永久代(PermGen)代之元空间(Metaspace)的意义
- JVM在JDK8取消了永久代(PermGen)代之元空间(Metaspace)的意义
- JAVA8.0 永久代(PermGen. ) ---> 元空间(Metaspace. )
- Java8内存模型—永久代(PermGen)和元空间(Metaspace)
- Java8内存模型—永久代(PermGen)和元空间(Metaspace)
- 永久代(PermGen)和元空间的区别(Metaspace)
- Java8内存模型—永久代(PermGen)和元空间(Metaspace)
- Java8内存模型—永久代(PermGen)和元空间(Metaspace)
- Java内存模型—永久代(PermGen)和元空间(Metaspace)
- Java8内存模型—永久代(PermGen)和元空间(Metaspace)
- Java8内存模型—永久代(PermGen)和元空间(Metaspace)
- 永久代(PermGen)和元空间的区别(Metaspace)
- Java8内存模型—永久代(PermGen)和元空间(Metaspace)
- Java8内存模型—永久代(PermGen)和元空间(Metaspace)
- Java8内存模型—永久代(PermGen)和元空间(Metaspace)
- Java8内存模型—永久代(PermGen)和元空间(Metaspace)
- Java8内存模型—永久代(PermGen)和元空间(Metaspace)
- ISTQB AL-TA/TTA连载系列03:问题驱动的软件测试设计全景图
- 直播 | 中小企业的微服务实践
- bootstrap table表头字段排序
- 《Spring 5 官方文档》26. JMS
- 我的学习路线
- JVM永久代(PernGem)和元空间(Metaspace)
- java.rmi.Naming和java.rmi.registry.LocateRegistry的区别
- 欢迎使用CSDN-markdown编辑器
- 乐高式微服务化改造
- Hive Bug集锦
- JAVA必背面试题和项目面试通关要点(带答案)
- STM32开发板+NOR+SRAM,STM32F103/407通用开发板PCB工程文件
- python在WIN下CMD运行中文乱码及python 2.x python 3.x编码问题
- 语义分割--End-to-End Instance Segmentation with Recurrent Attention