深入理解Java虚拟机(一): 内存区域
运行时数据区域
Java在运行时会把它所管理的内存划分为若干个不同的数据区域。
方法区(Method Area)
(1)线程共享
(2)存储
类信息
常量
静态变量
即时编译器编译后的代码
(3)非堆
(4)不需要连续的内存
(5)可以选择固定或可扩展
(6)可选择不实现垃圾收集
(7)针对常量池的回收
运行时常量池(Runtime Constant Pool)
a、常量池:存放编译期生成的字面常量和符号引用
b、这些常量池中的数据进入方法区后存放在运行时常量池中
c、动态性:运行期可将新的常量放入池中,例如:String.intern()方法
(8)对类型的卸载
(9)无法满足内存分配需求时会抛出OutOfMemoryError
虚拟机栈(VM Stack)
(1)线程私有
(2)生命周期与线程相同
(3)描述Java方法执行的内存模型
(4)栈帧
a、局部变量表:基本数据类型、对象引用和returnAddress类型(指向一条字节码指令的地址)
b、操作数栈
c、动态链接
d、方法出口
e、64位的long 和double会占用两个局部变量空间,其余的只占一个
f、编译期完成内存分配
g、方法运行期间不会改变局部变量表的大小
h、存储
i、入栈
j、出栈
(5)线程请求的栈深度大于虚拟机允许的深度会抛出StackOverFlowError
(6)如果扩展时无法申请到足够内存会抛出OutOfMemoryError
本地方法栈(Native Method Stack)
(1)为本地方法(Native Method)服务
(2)<--> 区别于虚拟机栈:为虚拟机执行Java方法服务。
(3)抛出StackOverFlowError
(4)抛出OutOfMemoryError
堆(Heap)
(1)Java虚拟机所管理的内存中最大的一块
(2)线程共享
(3)虚拟机启动时创建
(4)存放对象实例及数组
(5)垃圾回收管理(GC)主要区域
- 分代算法
| 新生代
eden
from
to
| 老年代
(6)可划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer,TLAB)
(7)可处于物理上不连续的内存空间,保证逻辑连续即可
(8)可通过-Xmx和-Xms控制大小(可扩展)
(9)堆无法再扩展时会抛出OutOfMemoryError
程序计数器(Program Counter Register)
(1)较小内存空间
(2)当前线程的行号指示器
(3)线程私有的内存
(4)唯一一个没有OutOfMemoryError的区域
直接内存(Direct Memory)
(1)NIO中使用基于通道(Channel)与缓冲区(Buffer)的I/O方式,使用Native函数库直接分配的堆外内存
(2)无法满足内存分配需求时会抛出OutOfMemoryError
对象的创建
(1)类加载检查机制
(2)为新生对象分配内存
(3)把一块确定大小的内存从堆中划分出来。
选择方式由Java堆是否规整决定
Java堆是否规整由垃圾收集器是否带有压缩整理功能决定。
由于对象创建是非常频繁的,则在并发情况下不是线程安全的
对分配内存空间的动作进行同步处理
把内存分配的动作按照线程划分在不同的空间中,即TLAB方式。
只有在TLAB用完并分配新TLAB的情况下需要同步锁定
可以使用-XX:+/-UseTLAB来设定
内存规整: 指针碰撞(Bump the Point)
内存不规整 空闲列表(Free List):维护列表并记录哪些是可用的。
(4)将分配到的内存空间初始化为0(不包含对象头)
(5)对对象进行必要的设置
(6)属于哪个类的实例
(7)如何找到类的元数据信息
(8)对象的哈希值
(9)对象的GC分代年龄
(10)这些数据存放在对象头(Object Header)中
(11)对象得到创建,但是所有的字段为零,在执行new之后会立即执行方法
对象的内存布局
(1)对象头(Object Header)
存储对象自身运行时数据(Mark Word),通常在32位和64位的机器上占用32bit和64bit的空间
哈希吗
GC分代年龄
锁状态标志
线程持有锁
偏向线程ID
偏向时间戳
(2)类型指针: 对象指向它的类元数据的指针
a、虚拟机通过这个指针来确定这个对象属于哪个类
b、不是所有的对象实例都保留有类型指针
c、如果对象是数组,还必须有一块用于记录数组长度的数据
(3)实例数据(Instance Data),真正存储的数据
存储顺序受到虚拟机分配策略参数(Fields Allocation Style)和字段在Java源码中的定义顺序影响
(4)对齐填充(Padding)
a、不是必要存在的
b、仅仅起占位作用
对象访问定位
Java通过栈上的引用(reference)数据来操作堆上的具体对象
句柄访问
(1)堆中划分一块内存作为句柄池
(2)reference中存储对象的句柄地址
(3)句柄中包含对象实例数据与类型数据各自的具体地址信息
优点:reference中存储稳定的句柄地址,在对象移动时只会改变句柄中的实例数据指针
直接指针
(1)考虑如何放置访问类型数据的相关信息
(2)reference中直接存储对象地址
优点:速度更快,节省一次指针定位的时间
本文转载自微信公众号【java学习之道】。
- 点赞
- 收藏
- 关注作者
评论(0)