首页 南方天气预报正文

复制粘贴快捷键,Java8内存模型—永久代(PermGen)和元空间(Metaspace)-雷火电竞登录

admin 南方天气预报 2019-11-05 231 0

一、JVM 内存模型

依据 JVM 标准,JVM 内存共分为虚拟机栈、堆、办法区、程序计数器、本地办法栈五个部分。

1、虚拟机栈:每个线程有一个私有的栈,跟着线程的创立而创立。栈里边存着的是一种叫“栈帧”的东西,每个办法会创立一个栈帧,栈帧中寄存了局部变量表(根本数据类型和目标引证)、操作数栈、办法出口等信息。栈的巨细能够固定也能够动态扩展。当栈调用深度大于JVM所答应的规模,会抛出StackOverflowError的过错,不过这个深度规模不是一个稳定的值,咱们经过下面这段程序能够测验一下这个成果:

栈溢出测验源码:


package com.paddx.test.memory;

public class StackErrorMock {
private static int index = 1;

public void call(){
index++;
call();
}

public static void main(String[] args) {
StackErrorMock mock = new StackErrorMock();
try {
mock.call();
}catch (Throwable e){
System.out.println("Stack deep : "+index);
e.printStackTrace();
}
}
}

运转三次,能够看出每次栈的深度都是不相同的,输出成果如下。

至于赤色框里的值是怎样出来的,就需求深化到 JVM 的源码中才干讨论,这儿不作具体论述。

虚拟机栈除了上述过错外,还有另一种过错,那便是当请求不到空间时,会抛出 OutOfMemoryError。这儿有一个小细节需求留意,catch 捕获的是 Throwable,而不是 Exception。因为 StackOverflowError 和 OutOfMemoryError 都不归于 Exception 的子类。

2、本地办法栈:

这部分首要与虚拟机用到的 Native 办法相关,一般状况下, Java 运用程序员并不需求关怀这部分的内容。

3、PC 寄存器:

PC 寄存器,也叫程序计数器。JVM支撑多个线程一起运转,每个线程都有自己的程序计数器。假使当时履行的是 JVM 的办法,则该寄存器中保存当时履行指令的地址;假使履行的是native 办法,则PC寄存器中为空。

4、堆

堆内存是 JVM 一切线程同享的部分,在虚拟机发动的时分就现已创立。一切的目标和数组都在堆上进行分配。这部分空间可经过 GC 进行收回。当请求不到空间时会抛出 OutOfMemoryError。下面咱们简略的模仿一个堆内存溢出的状况:


import java.util.ArrayList;
import java.util.List;

public class HeapOomMock {
public static void main(String[] args) {
// TODO Auto-generated method stub

List list = new ArrayList();
int i = 0 ;
boolean flag = true;
while(flag){
try {
i++;
list.add(new byte[1024*1024]);//每次添加一个1M巨细的数组目标
} catch (Throwable e) {
e.printStackTrace();
flag = false;
System.out.println("count="+i);//记载运转的次数
}
}
}

}
运转成果:
java.lang.OutOfMemoryError: Java heap spacecount=1708
at HeapOomMock.main(HeapOomMock.java:14)

每次运转的成果也都不相同

5、办法区:

办法区也是一切线程同享。首要用于存储类的信息、常量池、办法数据、办法代码等。办法区逻辑上归于堆的一部分,可是为了与堆进行差异,一般又名“非堆”。关于办法区内存溢出的问题会在下文中具体讨论。

二、PermGen(永久代)

绝大部分 Java 程序员应该都见过 "java.lang.OutOfMemoryError: PermGen space "这个反常。这儿的 “PermGen space”其实指的便是办法区。不过办法区和“PermGen space”又有着实质的差异。前者是 JVM 的标准,而后者则是 JVM 标准的一种完成,而且只要 HotSpot 才有 “PermGen space”,而关于其他类型的虚拟机,如 JRockit(Oracle)、J9(IBM) 并没有“PermGen space”。因为办法区首要存储类的相关信息,所以关于动态生成类的状况比较简单呈现永久代的内存溢出。最典型的场景便是,在 jsp 页面比较多的状况,简单呈现永久代内存溢出。咱们现在经过动态生成类来模仿 “PermGen space”的内存溢出:


package com.paddx.test.memory;

public class Test {
}
package com.paddx.test.memory;

import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;

public class PermGenOomMock{
public static void main(String[] args) {
URL url = null;
List classLoaderList = new ArrayList();
try {
url = new File("/tmp").toURI().toURL();
URL[] urls = {url};
while (true){
ClassLoader loader = new URLClassLoader(urls);
classLoaderList.add(loader);
loader.loadClass("com.paddx.test.memory.Test");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

运转成果如下:

本例中运用的 JDK 版本是 1.7,指定的 PermGen 区的巨细为 8M。经过每次生成不同URLClassLoader目标来加载Test类,然后生成不同的类目标,这样就能看到咱们了解的 "java.lang.OutOfMemoryError: PermGen space " 反常了。这儿之所以选用 JDK 1.7,是因为在 JDK 1.8 中, HotSpot 现已没有 “PermGen space”这个区间了,取而代之是一个叫做 Metaspace(元空间) 的东西。下面咱们就来看看 Metaspace 与 PermGen space 的差异。

三、Metaspace(元空间)

其实,移除永久代的作业从JDK1.7就开端了。JDK1.7中,存储在永久代的部分数据就现已搬运到了Java Heap或者是 Native Heap。但永久代仍存在于JDK1.7中,并没彻底移除,比如符号引证(Symbols)搬运到了native heap;字面量(interned strings)搬运到了java heap;类的静态变量(class statics)搬运到了java heap。咱们能够经过一段程序来比较 JDK 1.6 与 JDK 1.7及 JDK 1.8 的差异,以字符串常量为例:


package com.paddx.test.memory;

import java.util.ArrayList;
import java.util.List;

public class StringOomMock {
static String base = "string";
public static void main(String[] args) {
List list = new ArrayList();
for (int i=0;i< Integer.MAX_VALUE;i++){
String str = base + base;
base = str;
list.add(str.intern());
}
}
}

这段程序以2的指数级不断的生成新的字符串,这样能够比较快速的耗费内存。咱们经过 JDK 1.6、JDK 1.7 和 JDK 1.8 别离运转:

JDK 1.6 的运转成果:

JDK 1.7的运转成果:

JDK 1.8的运转成果:

从上述成果能够看出,JDK 1.6下,会呈现“PermGen Space”的内存溢出,而在 JDK 1.7和 JDK 1.8 中,会呈现堆内存溢出,而且 JDK 1.8中 PermSize 和 MaxPermGen 现已无效。因而,能够大致验证 JDK 1.7 和 1.8 将字符串常量由永久代搬运到堆中,而且 JDK 1.8 中现已不存在永久代的定论。现在咱们看看元空间到底是一个什么东西?

元空间的实质和永久代相似,都是对JVM标准中办法区的完成。不过元空间与永久代之间最大的差异在于:元空间并不在虚拟机中,而是运用本地内存。因而,默许状况下,元空间的巨细仅受本地内存约束,但能够经过以下参数来指定元空间的巨细:

-XX:MetaspaceSize,初始空间巨细,到达该值就会触发废物搜集进行类型卸载,一起GC会对该值进行调整:假如开释了很多的空间,就恰当下降该值;假如开释了很少的空间,那么在不超越MaxMetaspaceSize时,恰当进步该值。

-XX:MaxMetaspaceSize,最大空间,默许是没有约束的。

除了上面两个指定巨细的选项以外,还有两个与 GC 相关的特点:

-XX:MinMetaspaceFreeRatio,在GC之后,最小的Metaspace剩下空间容量的百分比,削减为分配空间所导致的废物搜集

-XX:MaxMetaspaceFreeRatio,在GC之后,最大的Metaspace剩下空间容量的百分比,削减为开释空间所导致的废物搜集

现在咱们在 JDK 8下从头运转一下代码段 4,不过这次不再指定 PermSize 和 MaxPermSize。而是指定 MetaSpaceSize 和 MaxMetaSpaceSize的巨细。输出成果如下:

从输出成果,咱们能够看出,这次不再呈现永久代溢出,而是呈现了元空间的溢出。

四、总结

经过上面剖析,我们应该大致了解了 JVM 的内存区分,也清楚了 JDK 8 中永久代向元空间的转化。不过我们应该都有一个疑问,便是为什么要做这个转化?所以,最终给我们总结以下几点原因:

1、字符串存在永久代中,简单呈现功能问题和内存溢出。

2、类及办法的信息等比较难确认其巨细,因而关于永久代的巨细指定比较困难,太小简单呈现永久代溢出,太大则简单导致老时代溢出。

3、永久代会为 GC 带来不必要的复杂度,而且收回功率偏低。

4、Oracle 或许会将HotSpot 与 JRockit 合二为一。

雷火电竞版权声明

本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。

韩东君,高盛:升港交所(0388.HK)目标价至235港元 评级“中性”-雷火电竞登录

  • 一点点,华控赛格11月7日盘中涨幅达5%-雷火电竞登录

    一点点,华控赛格11月7日盘中涨幅达5%-雷火电竞登录

  •   该行表明,注意到来自联营

  • 三体电影,瑞银:升港交所(0388.HK)目标价至250港元 评级“中性”-雷火电竞登录

  • 关悦,杜家毫在省住建厅调研:加速构建新式乡镇系统 助力打好三大攻坚战-雷火电竞登录

    关悦,杜家毫在省住建厅调研:加速构建新式乡镇系统 助力打好三大攻坚战-雷火电竞登录

  • 最近发表

    雷火电竞登录_雷火电竞安卓app_雷火苹果app

    http://www.tujidotimes.com/

    |

    Powered By

    使用手机软件扫描微信二维码

    关注我们可获取更多热点资讯

    雷火电竞出品