1.运行时数据区域
程序计数器
当前线程所执行字节码的行号指示器。
线程:私有
异常:无
虚拟机栈
1.局部变量表
2.操作数栈
3.动态链表
4.方法出口
线程:私有
异常:StackOverflowError,OutOfMemoryError
本地方法栈
供Native方法使用
线程:私有
异常:StackOverflowError,OutOfMemoryError
java堆
所有对象实例和数组在堆上分配内存,物理内存可以不连续。可细分为新生代,老年代,永久代(具体虚拟机有所不同)
线程:共享
异常:OutOfMemoryError
方法区
存储加载的类信息,常量,静态变量,即时编译后的代码,运行时常量池
线程:共享
异常:OutOfMemoryError
直接内存
是Native函数库直接分配堆外内存,NIO通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作,避免了在Java堆和Native堆中来回复制数据.
线程:共享
异常:OutOfMemoryError
2.对象
2.1对象的创建:
new指令 ->
检查方法区是否存在这个类的符号引用 ->
加载,解析,初始化 ->
分配内存 (类加载完成后便能确定分配内存大小) 指针碰撞,空闲列表->
设置对象信息(元数据信息,对象哈希表,对象GC分代年龄信息,对象初始化零值)->
怎么分配内存空间?
- 指针碰撞:假设java内存堆是规整的,已使用的内存放在一边,空闲内存放在另一边,中间用一指针作为分割,当分配内存时,指针移动相应大小。
- 空闲列表:虚拟机维护一个列表记录已使用和空闲内存记录,分配完成后,同步更新列表。
2.2对象的内存布局:
- 对象头:对象hashcode、分代年龄、锁状态标志、偏向线程ID、偏向时间戳,数组的长度
- 实例数据:
- 对齐填充:仅作为占位
2.3对象的访问定位
- 句柄:
栈内指针指向句柄池中对象的句柄,句柄保存对象地址,优势是在对象被移动(垃圾收集)只改变句柄中对象的地址,栈中reference本身不需要改变。 - 指针:
reference中存储的直接就是对象地址,优势是访问速度快,节省了一次指针定位的开销
总结:两种实现都很常见,具体看使用虚拟机的实现