Java虚拟机学习笔记——Java内存区域与对象创建
Posted <天各一方>
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java虚拟机学习笔记——Java内存区域与对象创建相关的知识,希望对你有一定的参考价值。
文章目录
Java内存区域
1.运行时数据区域
1.1 程序计数器
程序计数器是当前线程执行字节码的行号指示器。字节码解释器工作时需要通过改变这个计数器的值来选取下一条要执行的字节码指令。
多线程执行任务时,由于内核是通过切换来模拟异步操作,所以每条线程都需要一个独立的程序计数器,以便于线程切换后再次回到本线程时可以恢复到上次执行的位置。各条线程之间的计数器互不影响,独立存储,这类内存被称之为线程私有的内存。
1.2 Java虚拟机栈
该内存也是线程私有的,它描述了Java方法执行的内存模型。
在每个方法执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表,操作数栈,动态链接,方法出口等信息。每一个方法从调用到执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈与出栈的过程。
局部变量表存放了编译时就可知的各种基本数据类型(boolean,byte,char,short,int,float,long,double),对象引用。
局部变量在编译时就已经确定了内存空间,方法运行期间不会改变局部变量表的大小。
当线程请求的栈深度大于虚拟机栈所允许的深度,就会报出StackOverflowError异常。
1.3 本地方法栈
与Java虚拟机栈相似,区别是Java虚拟机栈执行Java方法,而本地方法栈执行虚拟机所使用到的Native方法服务。同时也属于线程私有的。
1.4 Java堆
Java堆是Java虚拟机所管理的内存中最大的一块。
Java堆是所有线程共享的一块内存区域,此内存区域的唯一目的就是存放对象的实例,几乎所有对象的实例都是在这里存放的。
Java堆是垃圾收集器管理的主要区域,很多时候被称为“GC堆”(Garbage Collected Heap),现在的收集器基本采用分代收集算法,所以Java堆可以细分为:新生代和老年代;再细分有Eden空间,FromSurvivor空间,ToSurvivor空间等。
1.5 方法区
属于各个线程共享的内存区域,用于储存已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。
运行时常量池属于方法区的一部分。用于存放编译时生成的各种字面量和符号引用。
- JDK8以前,虚拟机把GC分代设计扩展至方法区,使用永久代来实现方法区,省去了专门为方法区编写内存管理代码。
- JDK8之后,完全废弃永久代,改为元空间,在本地内存空间实现。将原本在常量池中的StringTable移至堆中。
2.虚拟机对象
2.1 对象的创建
Java虚拟机遇到new指令的时候,首先去检查一下这个指令的参数是否可以在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载,解析和初始化过。如过没有,那就先执行类加载过程。
类加载通过后,进行内存分配。有两种分配方式:第一种被称为指针碰撞。具体实现是所有被使用过的内存放在堆的一边,未被使用过的内存放在另一边。中间有一个指针作为“分界线”,当需要分配内存时,就将指针向未被分配的一边移动与对象大小相等的距离。这种分配方式针对Java堆中的内存是规整的。
另一种分配方式是空闲列表,这种分配方式针对Java堆中的内存空间不是规整的。虚拟机维护一个列表,记录了哪些内存块是可用的,分配的时候在列表上找到一块大小合适的空间分配给对象实例,并更新列表记录。
除了如何划分空间,还应该注意,对象的创建在虚拟机中是非常频繁的行为,即使仅仅只是修改一个指针的位置,在并发的情况下也是不是线程安全的。
要解决这个问题,主要有两种方法:一种是对分配内存的空间进行同步处理,——虚拟机采取的是CAS加上失败重试保证了分配动作的原子性。另一种方式是把内存分配的动作按照线程划分在不同的区域进行,即每个线程在Java堆上预先分配一小块内存,称为本地线程分配缓冲(TLAB),哪一个线程需要分配内存,就在自己的TLAB上分配,只有TLAB用完时,分配新的缓存区时才需要同步锁定。
分配完空间后,虚拟机将分配到的内存空间都初始化为零(不包括对象头),如果使用的TLAB,此操作可以提前至TLAB时。接着将对象进行必要的设置,如属于哪一个类的实例,哈希码…
完成后,从虚拟机的角度看,对象已经产生,但从Java程序的角度来看,对象的创建刚刚开始。接下来执行()方法,将对象按照程序员的意愿进行初始化。
2.2 对象的内存布局
对象在堆内存中的存储分为三大块:对象头,实例数据,对齐填充。
-
对象头包含两部分信息:第一部分储存对象自身运行时数据,如哈希码,GC年龄分代,锁状态标志等。第二部分是类型指针,即对象指向它类型元数据的指针,Java虚拟机通过这个指针来确定对象是哪个类的实例。但并不是所有虚拟机都保存这个类型指针。
-
实例数据是存储对象真正有效的信息。
-
对齐填充没有特别含义,只起到占位符的作用。HotSpot虚拟机自动管理内存要求对象起始地址必须是8字节的整数倍。
2.3 对象访问定位
分为两种方式:
- 句柄访问
优点:
reference中存储的是稳定的句柄地址,在对象被移动时只会改变句柄中的实例数据指针。 - 直接指针
优点:速度更快,节省一次指针定位的时间开销。
书籍:
《深入理解Java虚拟机》
以上是关于Java虚拟机学习笔记——Java内存区域与对象创建的主要内容,如果未能解决你的问题,请参考以下文章
深入理解JAVA虚拟机读书笔记——Java内存区域与内存溢出异常