APP下载

java内存区域

消息来源:baojiabao.com 作者: 发布时间:2024-11-01

报价宝综合消息java内存区域

jvm执行时资料区域

java虚拟机器在执行java程式的过程中将它所管理的内存划分为以下几个执行时资料区域:

程式计数器(Program Counter Register)虚拟机器栈(VM Stack)本地方法栈(Native Method Stack)堆(Heap)方法区(Method Area)执行绪私有区域(程式计数器、虚拟机器栈、本地方法栈),执行绪共享区域(堆、方法区),直接内存。执行绪私有资料区域生命周期与执行绪相同, 依赖使用者执行绪的启动/结束 而 建立/销毁(在 Hotspot VM 内)。执行绪共享区域随虚拟机器的启动/关闭而建立/销毁。直接内存并不是 JVM 执行时资料区的一部分。

1. 程式计数器

程式计数器是当前执行绪执行的字节码的行号指示器,为了执行绪切换后能够恢复到正确的执行位置,每条执行绪都需要一个独立的程式计数器,个执行绪之间的程式计数器互不影响,各自独立储存。这类内存区域为“执行绪私有”的区域。此内存区域是唯一一个没有OutOfMemoryError的区域。

2. 虚拟机器栈

虚拟机器栈描述的是Java方法执行的内存模型:每个方法在执行的同时都会建立一个栈帧(Stack Frame)用于储存区域性变量表、算子栈、动态连结、方法出口等资讯。每个方法从呼叫直至执行完成的过程,就对应着一个栈帧在虚拟机器栈中入栈到出栈的过程。栈容量只由-Xss引数设定

如果执行绪请求的栈深度大于虚拟机器所允许的最大深度,将丢掷StackOverFlowError 如果虚拟机器在扩充套件时无法申请到足够的内存空间,则丢掷OutOfMemoryError异常/**

* VM Args: -Xss160k

* -Xss的最小值为160k

**/

public class JavaVMStackSOF {

private int stackLength = 1;

public void stackLeak(){

stackLength++;

stackLeak();

}

public static void main(String[] args) throws Throwable {

JavaVMStackSOF oom = new JavaVMStackSOF();

try {

oom.stackLeak();

}

catch (Throwable e){

System.out.println("stack length : " + oom.stackLength);

throw e;

}

}

}

程式输出:

stack length : 772

Exception in thread "main" java.lang.StackOverflowError

at JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:11)

at JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:12)

3. 本地方法栈

本地方法栈与虚拟机器栈的作用相似,区别在于虚拟机器方法栈为虚拟机器执行Java方法服务,本地方法栈为虚拟机器使用Native方法服务。Hotspot虚拟机器将本地方法栈和虚拟机器方法栈合二为一。

4. 堆

Java堆是被所有执行绪共享的一块内存区域,在虚拟机器启动时建立。所有建立的物件和阵列都存放在堆上。Java堆是垃圾收集器管理的主要区域,垃圾收集器主要基于**分代收集算法**,因此从GC的角度对堆进行划分:新生代(Eden空间、From Survivor空间、To Survivor空间)、老年代。如果在堆中没有内存完成示例分配、并且堆也无法再扩充套件,将会丢掷OutOfMemoryError异常。堆的大小可以通过-Xmx(jvm执行时堆的最大值)和-Xms(jvm执行时堆的最小值)控制。

以下程式码测试堆的OutOfMemoryError异常,将堆的最小值-Xms引数与最大值-Xmx引数设定为一样即可避免堆自动扩充套件,引数-XX:+HeapDumpOnOutOfMemoryError可让虚拟机器在出现内存溢位是Dump出当前的内存堆转储快照以便时候进行分析。

/**

* VM Args: -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError

**/

public class HeapOOM {

static class OOMObject {

}

public static void main(String[] args) {

List list = new ArrayList();

while (true) {

list.add(new OOMObject());

}

}

}

输出如下:

java.lang.OutOfMemoryError: Java heap space

Dumping heap to java_pid3421.hprof ...

Heap dump file created [27644178 bytes in 0.129 secs]

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

at java.util.Arrays.copyOf(Arrays.java:3210)

at java.util.Arrays.copyOf(Arrays.java:3181)

at java.util.ArrayList.grow(ArrayList.java:261)

at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235)

at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)

at java.util.ArrayList.add(ArrayList.java:458)

at HeapOOM.main(HeapOOM.java:18)

-Xms : 设定堆的最小值,例如-Xms20, 分配20M-Xmx :设定堆的最大值,例如-Xmx200, 分配200M-XX:+HeapDumpOnOutOfMemoryError :让虚拟机器在出现内存溢位是Dump出当前的内存堆转储快照以便时候进行分析5. 方法区

方法区用于储存已被虚拟机器载入的类资讯、常量、静态变数、即时编译器编译后的程式码等资料。

执行时常量池(Runtime Constant Pool)是方法区的一部分。Class档案中除了有类的版本、字段、方法、界面等描述资讯外,还有一项资讯是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类载入后进入方法区的执行时常量池。java虚拟机器对class档案每一部分的格式都有严格的规定,每一个字节用于储存哪种资料都必须符号规范上的要求才会被虚拟机器认可、装载和执行。

既然执行时常量池是方法区的一部分,自然受到方法区内存的限制,当常量池无法再申请到内存时会丢掷OutOfMemoryError的异常,测试方法区和执行时常量池溢位:

/**

* jdk 1.8及之后,VM Args: -XX:MetaspaceSize=2m -XX:MaxMetaspaceSize=2m

* jdk 1.8及之前,VM Args: -XX:PermSize=2m -XX:MaxPermSize=2m

**/

public class RuntimeConstantPoolOOM {

public static void main(String[] args) {

List list = new ArrayList();

int i = 0;

while (true) {

list.add(String.valueOf(i++).intern());

}

}

}

程式输入:

Error occurred during initialization of VM

java.lang.OutOfMemoryError: Metaspace

>

6. 直接内存(Direct Memory)

直接内存(Direct Memory)并不是虚拟机器执行时的资料区的一部分,也不是java虚拟机器规范中定义的内存区域,不会受到java堆大小的限制,不受jvm gc的管理。这部分内存如果频繁使用,也可能导致OutOfMemoryError异常, 测试本机直接内存溢位,DirectMemory容量可通过-XX:MaxDirectMemorySize设定,如果不指定,预设与Java堆最大值(-Xmx指定)一样

/**

* VM Args: -Xmx20M -XX:MaxDirectMemorySize=10M

**/

public class DirectMemoryOOM {

private static final int _1MB = 1024 * 1024;

public static void main(String[] args) throws Exception {

Field unsafeField = Unsafe.class.getDeclaredFields()[0];

unsafeField.setAccessible(true);

Unsafe unsafe = (Unsafe) unsafeField.get(null);

while (true){

unsafe.allocateMemory(_1MB);

}

}

}

通过测试,始终没有丢掷OutOfMemoryError异常,测试环境jdk1.8, 开发工具IDEA, 目前还不知道为什么,如果有哪位朋友知道为什么,大家一起讨论下

结构梳理

引用参考

深入理解Java虚拟机器-周志明 著原文:https://www.cnblogs.com/pxset/p/11126585.html

2019-07-25 23:55:00

相关文章