虚拟机运行时数据区
Java虚拟机定义了在程序执行期间使用的各种运行时数据区域。
**线程共享数据区:**所有线程都能访问这块内存;随虚拟机启动而创建,虚拟机退出而销毁。
**线程隔离数据区:**每个线程有独立的空间;随线程的创建而创建,随线程退出而销毁。
程序计数器
Java的线程为了能从休眠状态唤醒时继续执行,需要保存当前执行的位置。JVM为每个线程都划分了一个独立的较小的内存空间,称之为
程序计数器(Program Counter Register,PC),用于存储正在执行的虚拟机字节码指令的地址(执行Native方法时为空)。
虚拟机栈
每个方法被执行的时候,虚拟机会创建一个**栈帧(StackFrame)用于存放局部变量、方法出口等信息。并入栈到Java虚拟机栈(Java Virtual Machine Stacks)**中,
方法执行完返回调用者的时候,将该栈帧出栈。因此每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
异常状况:
-
如果Java虚拟机栈容量可以动态扩展,当栈扩展时无法申请到足够的内存会抛出
OutOfMemoryError
; -
如果线程请求的栈深度大于虚拟机所允许的深度,将抛出
StackOverflowError
:HotSpot不支持扩容,因此在单线程中,只会因为栈容量无法容纳新的栈帧而导致
StackOverflowError
。/** * VM Args: -Xss128k */ public class JavaVMStackOF { private int stackLength = 1; public void stackLeak() { stackLength++; stackLeak(); } public static void main(String[] args) { JavaVMStackOF oom = new JavaVMStackOF(); try { oom.stackLeak(); } catch (Throwable e) { System.out.printl("stack length:" + oom.stackLength); throw e; } } }
-Xss128k :限制栈容量为128k
无限递归,直至无法再创建新的栈帧,观测结果:
stack length:2402 Exception in thread "main" java.lang.StackOverflowError at ...
-
没有足够的内存创建新线程时,会抛出
OutOfMemoryError
:/** * VM Args: -Xss2M */ public class JavaVMStackOOM { private void dontStop() { while (true) {} } public void stackLeakByThread() { while (true) { Thread thread = new Thread(new Runnable() { @Override public void run() { dontStop(); } }); thread.start(); } } public static void main(String[] args) { JavaVMStackOOM oom = new JavaVMStackOOM(); oom.stackLeakByThread(); } }
-Xss2M 每个线程的虚拟机栈大小设置为2M;
无限创建线程,观测结果:
Exception in thread "main" java.lang.OutOfMemoryError: unable to create native thread
堆
Java堆(Java Heap) 是被所有线程共享的一块内存区域,用于存放对象实例,在虚拟机启动时创建。
通过参数Xmx
和Xms
,可以扩展堆的大小。
异常状况:
如果在Java堆的容量已满,无法为新对象分配内存空间,并且堆也无法再扩展时,Java虚拟机将会抛出 OutOfMemoryError
。
/**
* VM args: -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
*/
public class HeapOOM {
static class OOMObject {}
public static void main(String[] args) {
List<OOMObject> list = new ArrayList<>();
while(true) {
list.add(new OOMObject());
}
}
}
-Xms20m -Xmx20m :限制堆大小为20M;
-XX:+HeapDumpOnOutOfMemoryError :出现OutOfMemoryError
时,Dump内存堆存储快照。
运行结果:
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid3404.hprof ...
Heap dump file created [22055981 bytes in 0.663 secs]
方法区
方法区(Method Area)用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
这区域的内存回收目标主要是针对常量池的回收和对类型的卸载。
异常状况:
如果方法区无法满足新的内存分配需求时,将抛出 OutOfMemoryError
。
本地方法栈
本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别只是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,
而本地方法栈则是为虚拟机使用到的本地(Native)方法服务。
异常状况:
与虚拟机栈一样,本地方法栈也会在栈深度溢出或者栈扩展失败时分别抛出 StackOverflowError
和 OutOfMemoryError
。