运行时数据区
java虚拟机在执行java程序时会将管理的内存分为不同的区域,根据jvm规范一共分为5个区域,如下图。
程序计数器
- 线程私有的,生命周期与线程一致
- 独立存储,互相隔离
- 存储的字节码指令的地址,字节码解释器通过程序计数器来选取下一个需要执行的指令
- 不会有OutOfMemoryError异常的情况
java虚拟机栈
- 线程私有的,生命周期与线程一致
- 独立存储,互相隔离
- 每一个方法被调用到完成的过程就是栈帧在java虚拟机栈中入栈到出栈的过程
- 存储编译期可知的局部变量表(基本数据类型,对象引用reference和returnAddress类型),操作栈,动态链接,方法出口等信息
- 超出栈深度StackOverflowError,栈动态扩展无法申请内存空间OutOfMemoryError
本地方法栈
本地方法栈与java虚拟机栈很相似,其区别不过是本地方法栈是为Native方法服务的。堆
- 线程共享区域
- 虚拟机启动是创建
- 存放对象实例
- 可细分为新生代和老年代,在细致一点可以分为Eden空间,From Survivor空间,To Survivor空间等。
- 堆内存可以是物理上不连续的内存空间,只要逻辑上是连续的即可。可以通过-Xmx和-Xms控制堆大小。
- 如果堆内没有内存来分配对象实例,并且再也无法扩展时,抛出OutOfMemoryError
- 方法区
- 线程共享
- 存储已被虚拟机加载的Class信息,如类名,访问修饰符,常量池,字段描述,方法描述等。
- 堆内存可以是物理上不连续的内存空间,只要逻辑上是连续的即可。
- 抛出OutOfMemoryError
- 可通过-XX:PermSize 和 -XX:MaxPermSize 来限制方法区的大小。
直接内存
直接内存并不是虚拟机运行时数据区的一部分,也不是java虚拟机所定义的。
在jdk1.4新加入的NIO类,引入了一种基于通道(channel)与缓存区(buffer)的I/O方式,它可以直接使用Native函数库直接分配堆外内存,然后通过一个存储在java堆里的DirectByteBuffer对象作为这块内存的引用操作。这样能在一些场景显著提高性能,因为避免了在java堆和Native堆中来回复制数据。虽然这块内存不是虚拟机直接管理,但如果超过了本机物理内存的大小也会抛出OutOfMemoryError。
对象访问
在java虚拟机中,Object obj = new Object()这段代码在创建对象的时候是如何分配内存的呢?“Object obj”这部分语句定义将会反映到java虚拟机栈的局部变量表中,作为一个reference类型数据出现。而“new Object()”这部分语句定义将会反映到java堆中,形成一块存储了Object类型的所有实例数据值的结构化内存。另外,在java堆中还必须包含能查找到此对象类型数据(如对象类型,父类,实现的接口,方法等)的地址信息,这些类型数据则存储在方法区中。