java虚拟机(JVM)是java平台的基石,任何java程序都是在JVM上运行的。java编译器把java程序编译成class文件,然后把class文件载入JVM中运行,JVM屏蔽了底层的硬件,所有的class文件只在JVM中运行,实现了一次编写到处运行(Write Once Run Anywhere)的宏伟目标。
JVM内部结构包括:
- 类加载子系统
- 运行时数据区
- 执行引擎
JVM运行时数据区结构
文章图片
- 方法区
虚拟机共享的数据区,每个虚拟机只有一个方法区。主要存储已被虚拟机加载的类的信息、常量、静态变量等数据。 - 堆区
虚拟机共享的数据区,每个虚拟机只有一个堆区。java 堆是虚拟机管理的内存中最大的一块区域,存储所有的对象实例以及数组。由于堆区是多线程共享的,所以不是线程安全的。 - 栈区
每个线程都有自己独立的栈区,栈区是线程安全的,是java方法执行的内存模型。每个方法在执行时都会创建一个栈帧(stack frame)用于存储本地变量表、操作数栈、帧数据等。随着方法调用的结束,相应的栈帧数据也随之删除。 - 程序计数器
每个线程都有自己独立的程序计数器,指向当前指令的地址。 - 本地方法栈
每个线程都有自己独立的本地方法栈,用于存储调用本地方法(native method)的信息。
堆区溢出 java堆用于存储对象实例,只要不断的创建对象,并保证避免垃圾回收机制清除这些对象,在达到最大堆容量限制的时候就会产生内存溢出异常。-Xmx10m参数表示把堆区的最大容量变小调成10M,-XX:+HeapDumpOnOutOfMemoryError参数表示生成堆区快照文件。
vm 参数:-Xmx10m -XX:+HeapDumpOnOutOfMemoryError
import java.util.ArrayList;
import java.util.List;
/**
* vm args: -Xmx10m -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 表示java堆区空间溢出,说明了对象是存储在堆区中的。
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid10876.hprof ...
Heap dump file created [13387910 bytes in 0.036 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 jvm.HeapOOM.main(HeapOOM.java:17)
在项目的路径下会生成 java_pid10876.hprof 文件,这个文件就是dump出来的堆转储快照文件。我们用Memory Analyzer打开看一下。
文章图片
点击上图最下面的details链接可以看到更详细的信息:
文章图片
可以看到OOMObject对象占了93%的内存,罪魁祸首就是它了。
栈区溢出 每个方法在执行时都会创建一个栈帧,-Xss160k参数表示栈区的容量为160k,然后递归调用方法,让栈区溢出。
vm 参数: -Xss160k
/**
* vm args: -Xss160k
*/
public class JavaVMStackSOF {static int stackLength = 1;
/**
* 递归调用,使其超出栈的最大深度
*/
public void stackLeak() {
stackLength++;
stackLeak();
}public static void main(String[] args) {JavaVMStackSOF javaVMStackSOF = new JavaVMStackSOF();
try{
javaVMStackSOF.stackLeak();
}catch (Throwable e) {
System.out.println("stack length: " + stackLength);
throw e;
}
}
}
输出结果 Exception in thread “main” java.lang.StackOverflowError 表示java栈区空间溢出,说明了方法调用是存储在栈区中的。
stack length: 772
Exception in thread "main" java.lang.StackOverflowError
at JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:12)
at JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:13)
at JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:13)
at JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:13)
at JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:13)
......
了解了一些JVM的知识对我们平时编程还是很有用的哦,比如看到java.lang.OutOfMemoryError: Java heap space 异常就知道是堆区溢出了,可能是对象创建太多了;看到java.lang.StackOverflowError异常就知道是栈区溢出了,马上去查查是不是方法调用有问题。
这样定位问题是不是更精准了呢?
【计算机基础|初识java虚拟机(JVM)运行时数据区结构】
文章图片
推荐阅读
- JVM|JVM系列之运行时数据区与内存异常演示
- 学习笔记|JVM学习笔记(二)运行时数据区
- 笔记|设计模式之一——单例模式
- Java|(JUC 下典型的类)Java 并发包中线程同步器
- 笔记|并发编程中常见的锁策略<包含详细介绍CAS机制和ABA问题>
- 字符串|2022.04.29(LC_680_验证回文字符串 Ⅱ)
- JVM|初识JVM(JVM运行流程,JVM运行时数据区,内存布局中的异常)
- 树【二叉树,红黑树,B树,B+树】
- java|vue - ES6模块化、promise、webpack打包(所在在学的朋友们先看这篇,看了不吃亏)...