被废弃的耐久代
想起之前口试的时候有口试官问起过我一个问题:Java 8为什么要废弃耐久代即Metaspace的浸染。由于其时利用的Java 7且研究重心不在JVM上,一下没有答复上来,本日溘然想起这个问题,就具体总结一下这个问题。
首先我们看一张JVM内存机关的图:
苏州软件定制开拓 前的版本" src="http://www.importnew.com/https:/images2018.cnblogs.com/blog/801753/201804/801753-20180401164846766-809607195.png" />
留意到内里有一块METHOD AREA,它是一块线程共享的工具,昆山软件开发,名为要领区,在HotSpot虚拟机中,这块METHOD AREA我们可以认为等同于耐久代(PermGen),在Java 6及之前的版本,耐久代存放了以下一些内容:
到了Java 7之后,常量池已经不在耐久代之中举办分派了,而是移到了堆中,即常量池和工具共享堆内存。
接着到了Java 8之后的版本(至此篇文章,Java 10刚宣布),耐久代已经被永久移除,取而代之的是Metaspace。
为什么要移除耐久代
HotSpot团队选择移除耐久代,有内因和外因两部门,从外因来说,我们看一下JEP 122的Motivation(念头)部门:
This is part of the JRockit and Hotspot convergence effort. JRockit customers do not need to configure the permanent generation (since JRockit does not have a permanent generation) and are accustomed to not configuring the permanent generation.
大抵就是说移除耐久代也是为了和JRockit举办融合而做的尽力,JRockit用户并不需要设置耐久代(因为JRockit就没有耐久代)。
从内因来说,耐久代巨细受到-XX:PermSize和-XX:MaxPermSize两个参数的限制,劳务派遣管理系统,而这两个参数又受到JVM设定的内存巨细限制,这就导致在利用中大概会呈现耐久代内存溢出的问题,因此在Java 8及之后的版本中彻底移除了耐久代而利用Metaspace来举办替代。
Metaspace
上面说了,为了制止呈现耐久代内存溢出的问题,Java 8及之后的版本彻底移除了耐久代而利用Metaspace来举办替代。
Metaspace是要领区在HotSpot中的实现,它与耐久代最大的区别在于:Metaspace并不在虚拟机内存中而是利用当地内存。因此Metaspace详细巨细理论上取决于32位/64位系统可用内存的巨细,昆山软件公司,可见也不是无限制的,需要设置参数。
接着我们模仿一下Metaspace内存溢出的环境,前面说了耐久代存放了以下信息:
所以最简朴的模仿Metaspace内存溢出,我们只需要无限生成类信息即可,类占据的空间老是会高出Metaspace指定的空间巨细的,下面用Cglib来模仿:
public class MetaspaceOOMTest { /** * JVM参数:-XX:MetaspaceSize=8m -XX:MaxMetaspaceSize=128m -XX:+PrintFlagsInitial */ public static void main(String[] args) { int i = 0; try { for (;;) { i++; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(OOMObject.class); enhancer.setUseCache(false); enhancer.setCallback(new MethodInterceptor() { public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { return proxy.invokeSuper(obj, args); } }); enhancer.create(); } } catch (Exception e) { System.out.println("第" + i + "次时产生异常"); e.printStackTrace(); } } static class OOMObject { } }
虚拟机参数配置为”-XX:MetaspaceSize=8m -XX:MaxMetaspaceSize=128m“,运行代码,功效为:
第15562次时产生异常 net.sf.cglib.core.CodeGenerationException: java.lang.reflect.InvocationTargetException-->null at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:345) at net.sf.cglib.proxy.Enhancer.generate(Enhancer.java:492) at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:114) at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:291) at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:480) at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:305) at org.xrq.commom.test.jvm.MetaspaceOOMTest.main(MetaspaceOOMTest.java:34) Caused by: java.lang.reflect.InvocationTargetException at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at net.sf.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:413) at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:336) ... 6 more Caused by: java.lang.OutOfMemoryError: Metaspace at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(Unknown Source) ... 11 more
可见纵然利用了Metaspace,也是有OOM的风险的,可是由于Metaspace利用本机内存,因此只要不要代码内里犯太初级的错误,OOM的概率根基是不存在的。
Metaspace相关JVM参数