说明:主要参照 《深入理解Java虚拟机--JVM高级特性与最佳实践》一书,以作记录学习的过程。
一:概述
对于C和C++程序员来说,拥有着对每个对象的的“所有权”,而Java则是通过虚拟机来自动管理内存;所以一旦Java虚拟机出现内存泄漏和溢出方面的问题时,如果不了解虚拟机怎样使用内存,那么排查错误将会成为一项异常艰难的工作。
二:运行时数据区域
Java虚拟机在执行Java程序过程中会把把它管理的内存划分为若干个不同的数据区域 。如下图所示:
各个区域都有各自的用途,以及创建和销毁时间。
1.程序计数器:一块较小的内存空间,是当前线程所执行的字节码的行号指示器。为了在多线程环境下,线程切换后能恢复到正确的执行位置,所以每条线程都需要有一个独
立的程序计数器。
2.Java虚拟机栈:线程私有,生命周期与线程相同;
虚拟机栈描述的是Java方法执行的内存模型:每个方法被执行的时候都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
该部分规定了两种异常状况(测试代码如下):StackOverflowError(线程请求的栈深度大于虚拟机所允许的深度);
OutOfMemoryError(如果虚拟机栈可动态扩展,在扩展时无法申请到足够的内存时)
public static void main(String[] args) {
//该句会出现java.lang.StackOverflowError 异常
testStackOverflowError();
//以下代码会出现java.lang.OutOfMemoryError 异常
List list = new ArrayList();
for (;;) {
int[] temp = new int[1000000];
list.add(temp);
}
}
public static void testStackOverflowError(){
for (;;){
testStackOverflowError();
}
}
3.本地方法栈:与虚拟机栈作用相似。
4.Java堆:被所有线程共享的一个区域,也是Java虚拟机所管理的的内存中最大的一块,该区域唯一的目的就是存放对象实例。
还可以细分为:新生代和老年代;再细分可分为Eden空间、From Survivor空间、To Survivor空间(分代收集算法)。
如果堆中没有内存完成实例分配,且无法再扩展时,将会抛出OutOfMemoryError异常。
5.方法区:同Java堆一样是线程共享的内存区域,用于存储已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。(虽然Java虚拟机规范将方法区描述为堆的一个逻辑部分,但它却有一个别名:Non-Heap,即非堆);
当方法区无法满足内存分配需求时,将抛出OutOfMemoryError。