前文已经了解了对象的创建过程,对象内存空间的回收,在多线程环境下共享变量的同步等虚拟机知识,接下来我们来看下在虚拟机中是如何进行内存管理的,也就是Java虚拟机运行时内存分区。
运行时内存分区
如上图所示,在Java虚拟机运行时按照内存是否私有可以将内存空间分为线程私有和线程共享两大块,其中 方法区 和 堆 为 线程共享 空间, 虚拟机栈 , 本地方法栈 , 程序计数器 为 线程私有 空间。
方法区
方法区主要用于存储已被加载的类,静态变量,常量和即时编译器编译后的代码等数据,是被线程共享的内存空间。运行时常量池属于方法区的一部分,用于存放编译器生成的各种字面常量和符号引用,当方法区内存不足时,会抛出OutOfMemoryError异常,在对象创建过程及类加载器这两节中,我们其实已经接触过运行时常量池了,如下图:
堆
堆空间是被线程共享的内存区域,其主要用于存放新创建的对象实例,大多数通过new指令创建的对象都在堆空间,如果创建对象是,堆上没有空间可以完成分配,就会抛出OutOfMemoryError异常,同时我们前面所说的分代回收也指的是在堆空间上针对不同年龄对象进行内存释放的策略。
虚拟机栈
虚拟机栈是线程私有的,其生命周期与线程相同,绑定唯一线程,当线程销毁时,对应的虚拟机栈所占用的空间也会被释放。在虚拟机栈中针对每一个执行的方法都会形成一个栈帧,这个方法从开始调用到调用结束正好对应着相关栈帧在虚拟机栈中出栈入栈的过程,虚拟机栈结构如下图所示:
从数据结构可以看出,当线程的方法调用超过虚拟机栈的最大深度,会抛出StackOverflowError异常,如果虚拟机栈实现是支持动态扩展的,扩展时申请不到内存会抛出OutOfMemoryError异常
本地方法栈
本地方法栈也是线程私有的,其作用与虚拟机栈类似,主要用于记录native方法调用,与虚拟机栈一样,本地方法栈也可能抛出StackOverflowError和OutOfMemoryError异常
程序计数器
程序计算器也是线程私有的,主要用于记录当前线程执行的位置,在线程休眠或者CPU被抢占导致线程暂停时,下次恢复执行时,通过程序计数器确定所要执行的指令位置等信息,该内存区域是唯一一个在Java虚拟机中没有规定任何OutOfMemoryError异常的区域。
完整内存分区
结合本文和对象管理中的内容,我们可以看到完整的内存分区如下图所示: