Java的运行时数据区被划分为5给部分,分别是:
程序计数器
java虚拟机栈
本地方法栈
Java堆
方法区
前面三个是线程私有的,后面两个是线程共享的。
1、程序计数器
程序计数器是一块很小的空间,Java虚拟机规范没有规定此内存会产生OOM。
它是当前线程执行到哪里的行号指示器,可以理解为是当前线程执行的字节码的地址。字节码解释器在工作时就是通过改变这个计数器来选取下一条需要执行的指令。
在多线程环境下,线程切换时会保存当前程序计数器的值,以便在线程恢复执行的时候能够从上次切换的地方开始执行。所以这也需要每个线程都拥有一个程序计数器,即线程私有。
另外,在执行本地方法时,程序计数器的值为空(undefined)
2、Java虚拟机栈
Java虚拟机栈也是线程私有的,它描述的是Java方法执行的内存模型,在执行一个Java方法的时候,会创建一个栈帧。栈帧存储了局部变量表,操作数栈,动态链接,方法出口。
局部变量表存放了编译时期就知道的基本数据类型,对象引用(Reference类型的,就是指向对象的指针),局部变量表的需要的空间在编译期间就确定了,并且在运行期间不会改变。
这里会产生两种异常:
StackOverflowError:线程请求栈的深度超过虚拟机规定的最大深度(即方法调方法)
OutOfMemoryError:如果虚拟机栈设置了动态扩展,那么在扩展时没有足够的内存了,就会抛出OOM
3、本地方法栈
类似Java虚拟机栈,上面是普通的Java方法,这里是native方法。同样也有这两个异常。
4、Java堆
Java堆是对象分配的区域,也是垃圾频繁回收的区域,所以也叫GC堆,它是线程共享的。
所有的对象实例以及数组都要在堆上分配(现在也不一定了。。。时代在发展啊)
Java堆按垃圾回收可以分为新生代和老年代,进一步可以划分为Eden区,From Survivor区,To Survivor区。
从线程共享角度来说,堆中还可以划分出线程私有的分配缓冲区:Thread Local Allocation Buffer
以上两种划分都和存放的内容无关,存放的都是对象实例。
Java堆可以处于物理上不连续逻辑上是连续的。堆的大小是可扩展的,用-Xmx -Xms控制。没内存了会抛出OOM
5、方法区
线程共享的区域,存储了已加载的类信息,常量,静态变量,即时编译器编译后的代码。别名叫非堆(non-heap)
方法区很少发生gc,gc的时候主要是对常量池和类型卸载进行回收,回收效率低。
常量池的内容并非在编译期才能产生,也可以在运行时动态产生,例如String.intern().