Java内存区域与对象--《深入理解Java虚拟机》学习笔记

时间:2023-01-02 14:15:20

Java内存区域与对象《深入理解Java虚拟机》学习笔记

Java内存区域与对象--《深入理解Java虚拟机》学习笔记
一、java内存区域
- 程序计数器
程序计数器是一块较小的线程私有的内存区域,存储当前线程所执行的字节码指令的地址,字节码解释器就是通过改变该计数器的值来选取下一条需要执行的字节码指令;如果线程正在执行的是一个java方法,程序计数器记录的是正在执行的虚拟机自己吗指令的地址;如果正在执行的是native方法,程序计数器为空;此内存区域是唯一没规定OutOfMemoryError的区域。
- Java虚拟机栈
虚拟机栈是线程私有的内存区域,是描述java方法执行的内存模型:每个方法运行时都会创建一个栈帧,存储局部变量表、操作数表、动态链接和方法出口信息等;方法从调用到完成对应一个栈帧在虚拟机栈中的入栈和出栈。通常说的“堆栈”中的“栈”指的是虚拟机栈中局部变量表部分;局部变量表存储了编译器可知的各种基本数据类型(boolean、byte、long等)、对象引用和returnAddress类型(指向一个字节码指令的地址)。该区域两种异常:如果线程请求的栈深度大于虚拟机允许的深度,抛出*Error异常;如果虚拟机可以动态拓展,拓展时无法申请到足够内存,抛出OutOfMemoryError异常。
- 本地方法栈
本地方法栈类似于虚拟机栈,只不过本地方法栈面向的是native方法。
- java堆
java堆是线程共享的一块内存区域,存储对象实例(包括数组),是垃圾收集器管理的主要区域;根据规定,该区域可以处于物理上不连续的内存空间,只要逻辑连续即可,可以通过(-Xmx和-Xms)动态扩展,如果在堆中没有内存完成实例分配,并且堆无法继续扩展时,抛出OOM。
- 方法区
方法区是线程共享的区域,存储已被虚拟机加载的类的信息、常量、静态变量、即时编译器编译的代码等数据;垃圾收集器对该区域的管理较少,回收的目标主要是常量池的回收和类型的卸载。该区域异常OOM。
- 运行时常量池
运行时常量池是方法区内存的一部分,存储编译期生成的各种字面量和符号引用、翻译出来的直接引用;该区域具有动态性,可以在运行期将新的常量放入常量池。该区域异常OOM。
- 直接内存
直接内存不是虚拟机运行时数据区的一部分,也不是java虚拟机规范中定义的内存区域,是一块堆外内存区域,可以通过java堆中的DirectByteBuffer对象持有该区域的引用进行操作。该区域异常OOM。
二、java对象相关
- 对象的创建过程
-类加载检查:检查new指令参数是否在常量池中有一个类的符号引用,并检查这个符号引用的类是否已被加载、解析和初始化过,如果没有,执行类加载过程;
-为新生对象分配内存:分配内存策略根据java堆内存是否规整分为“指针碰撞”和“空闲列表”两种;分配内存还需考虑线程安全问题,两种解决方案:分配内存动作进行同步(CAS配上失败重试保证更新操作的原子性)和本地线程分配缓冲(缓冲内存自动初始化为零值);
-虚拟机对对象进行必要的设置;
-init方法开始执行,初始化一个真正可用的对象;
2.对象的内存布局
对象在内存中的布局分为:对象头、实例数据、对齐填充
- 对象头:存储对象自身的运行时数据(哈希码、GC分代年龄、偏向线程ID等)、类型指针(指向类的元数据);
- 实例数据:真正有效信息,程序代码中定义的各类型字段;
- 对齐填充:非必须存在,起占位符作用,JVM要求对象起始地址为8字节的整数倍;
3.对象的访问定位
对象实例存储在堆内存中,java栈本地变量表中持有对象的引用,主流访问方式:句柄访问、直接指针访问;
- 句柄访问:java堆分出一块内存作为句柄池,reference(本地变量表)存储对象的句柄地址,如图Java内存区域与对象--《深入理解Java虚拟机》学习笔记
- 直接指针访问:reference存储对象的地址,如下图Java内存区域与对象--《深入理解Java虚拟机》学习笔记