Java内存划分
Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域,如下图
一、程序计数器
程序计数器(Program Counter Register)是一块很小的内存空间,就像计算机组成原理的PC寄存器,它可以看作当前线程所执行的字节码的行号指示器,解释器通过改变计数器的值来实现分支、循环等操作。为了防止线程切换之后找不到原来执行的位置,每个线程都单独拥有一个程序计数器,可以称这类内存为“线程私有”内存。
二、Java虚拟机栈
它和程序计数器一样是线程私有的,当每个方法执行的时候Java虚拟机都会产生一个栈帧(Stack Frame),栈帧中存储了局部变量表(注:进入一个方法的时候,方法所需要的局部变量大小是确定的,不会再改变)、操作数栈、动态链接、方法出口等信息)
在Java虚拟机创建完栈帧之后就会把它入栈到虚拟机栈中,当执行完毕后就将栈帧出栈,因此当无限递归时,会创造大量的栈帧进入到虚拟机栈中,就会产生StackOverflow异常public static void stackLeak(){
stackLeak();
}
public static void main(String[] args) {
stackLeak();
}
运行结果为
除了上述情况,当使用的Java虚拟机允许动态扩展虚拟机栈的时候,就会一直申请内存扩展虚拟机栈,直到无法申请到足够的内存,这时候便会产生OutOfMemoryError异常
三、本地方法栈
本地方法栈与虚拟机栈十分相似,虚拟机栈为Java方法提供服务,而本地虚拟机栈为本地方法服务,在栈深度溢出的时候也会产生StackOverflowError或者OutofMemoryError
四、Java堆
Java堆与虚拟机栈不同,它是所有线程共享的一片区域,它只有一个功能就是存放对象实例,我们平时使用的对象变量里面存放的则是对象地址,它指向堆中的对象实例。
一直循环生成新的对象便会造成Java堆溢出,而产生OutOfMemory异常static class OOMObject{
}
public static void main(String[] args) {
//使用list保持引用,防止GC回收对象
List list= new ArrayList();
while(true){
list.add(new OOMObject());
}
}
使用虚拟机参数-Xms20m -Xmx20m限制堆大小为20m不可扩展,运行上述程序后结果为
可以看出一直产生新对象,就会占满堆内存
五、方法区
方法区与Java堆一样,也是各个线程所共享的区域,它存储了每一个类的结构信息(Class文件),例如运行时常量池,字段和方法数据,构造函数和普通方法的字节码内容,还包括一些在类,实例,接口初始化时用到的特殊方法。
当限制方法区大小并且不断加载类文件的时候,方法区就会溢出,从而产生OutOfMemory异常