Java Memory Model ,JMM Java内存模型
(图片来源于网络,侵删。)
- 程序计数器
- Java虚拟机栈
- 本地方法栈
- 堆
- 方法区
程序计数器,Java虚拟机栈,本地方法栈是线程私有的。
方法区,堆是线程共享的。
- 程序计数器
这块区域是虚拟机规范中唯一没有OutOfMemory错误的区域
- Java虚拟机栈
描述的是Java方法执行的内存模型:每个方法执行的同时会创建一个栈帧。
栈帧存储了方法的局部变量表、操作数栈、动态连接和方法返回地址等信息。
1)线程请求的栈深度大于虚拟机允许的栈深度,将抛出StackOverflowError
2)虚拟机栈空间可以动态扩展,当动态扩展无法申请到足够的空间时,则抛出OutOfMemory异常
本地方法栈
本地方法栈与虚拟机栈发挥的作用十分相似,区别是虚拟机栈执行的是Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的native方法服务,可能底层调用的c或者c++,我们打开jdk安装目录可以看到也有很多用c编写的文件,可能就是native方法所调用的c代码。堆
堆是java虚拟机管理内存最大的一块内存区域,因为堆存放的对象是线程共享的,所以多线程的时候也需要同步机制
它是所有线程共享的,它的目的是存放对象实例。同时它也是GC所管理的主要区域,因此常被称为GC堆。当前主流的虚拟机如HotPot都能按扩展实现(通过设置 -Xmx和-Xms)。
如果堆中没有内存内存完成实例分配,而且堆无法扩展将报OOM错误(OutOfMemoryError)
- 方法区
用于存储已被虚拟机加载的类信息、常量、静态变量,如static修饰的变量加载类的时候就被加载到方法区中。
运行时常量池
是方法区的一部分,class文件除了有类的字段、接口、方法等描述信息之外,还有常量池用于存放编译期间生成的各种字面量和符号引用。
溢出的两个基本例子:
public class MemErrorTest {
public static void hi(){
hi();
}
public static void main(String[] args) {
try{
List<Object> list = new ArrayList<Object>();
for(;;){
//创建对象速度可能高于JVM
list.add(new Object());
}
}catch (OutOfMemoryError e){
e.printStackTrace();
}
try{
//递归造成StackOverflowError 这边因为每运行一个方法将创建一个栈帧
hi();
}catch (StackOverflowError e){
e.printStackTrace();
}
}
}
运行结果:
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:265)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:239)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:231)
at java.util.ArrayList.add(ArrayList.java:462)
at menerror.MemErrorTest.main(MemErrorTest.java:24)
java.lang.StackOverflowError
at menerror.MemErrorTest.hi(MemErrorTest.java:16)
at menerror.MemErrorTest.hi(MemErrorTest.java:16)
at menerror.MemErrorTest.hi(MemErrorTest.java:16)
……