内存局域
Java虚拟机所管理的内存包括以下几个运行时内存区域。
程序计数器
线程私有。记录线程正在执行的字节码指令的地址,用于虚拟机在各个线程之间切换时快速恢复到执行位置,在分支,循环,跳转,异常处理线程恢复等基础功能都需要依赖这个计数器来完成。但如果执行的是Native方法,这个计数器的值为undefined,他是唯一没有OutOfMemoryError的区域。
Java虚拟机栈
线程私有。也是我们平时所说的“堆内存”与“栈内存”中的“栈内存”,生命周期与所属线程同生共死, 用于存放栈帧。两种异常:如果线程请求的栈内存大于虚拟机规定的,将抛出StackOverflowError。不过虚拟机大都是可以动态扩展的,一直到扩展而再也申请不到足够内存时,则OutOfMemoryError。
- 栈帧
每个方法在执行的同时都会创建一个栈帧,生命周期:方法从调用到执行完成对应一个栈帧在虚拟机栈中的入栈与出栈。存储局部变量表,操作数栈,动态链接,方法出口等信息。
- 局部变量表
存放编译期可知的各种基本数据类型(8种,boolean,byte,char,short...),对象引用(reference类型,指向对象的指针,句柄或其他),returnAddress类型(指向一条字节码指令的地址)。
native栈
线程私有。功能与native方法类似,不同点native栈作用于native方法,java虚拟机栈作用于java方法。有些虚拟机上两者合二为一。
java堆
线程共享,虚拟机管理内存中的最大一块。所有对象实例,数组都在堆上分配(非绝对)。垃圾收集器管理的主要区域。异常:OutOfMemoryError
方法区
线程共享,存储虚拟机加载的类信息,常量池,静态变量,即时编译器后的代码等数据。
- 常量池
属于方法区,存储编译期生成的各种字面量和符号引用。
HotSpot虚拟机在java堆上的分配,布局和访问过程
普通对象创建
虚拟机遇到一条new指令时
1.检查常量池中是否有对应类的引用,并检查是否被加载,解析和初始化过,如果没有先进行相应类的加载过程。
2.分配内存。
“指针碰撞”:假设java堆内存时绝对工整的,已用内存在一边,未用内存在另一边,指针中间标识,分配内存只需移动指针。
“空闲列表”:java堆内存分配不规整,由一个表来记录已用未用,分配内存时找一块足够大小的内存。
(两种由所采用的垃圾收集器是否带有压缩整理功能决定)
线程安全:(1):同步锁,(2):在对上预先给每个线程留下空间,平时在这个空间上分配,空间不够了再同步。
3.虚拟机设置对象的基本信息。对象头,实例数据,对其填充。
4.<init>构造函数
内存布局
- 对象头
存储自身运行时数据:hashCode, GC分代年龄,锁状态等等;
类型指针:指向它的类元数据的指针
图片引用地址:https://blog.csdn.net/zhoufanyang_china/article/details/54601311
- 实例数据
对象真正存储的有效信息,各种字段的值。HotSpot默认顺序 longs/doubles,ints , shorts/chars , bytes , booleans ,oops .
- 对其填充
占位符的作用。对象的大小必须是8字节的整数倍,没对齐的补齐。
对象的方位定位
java程序通过栈上reference数据使用堆上的对象。访问对象方式取决于虚拟机的实现,目前主流句柄和直接指针两种。
- 句柄
- 直接指针