Java JVM解析

Java 发表评论
类加载的过程:类从被加载到虚拟机内存开始,到被卸载出内存为止,生命周期是:加载、验证、准备、解析、初始化、使用和卸载。
 加载阶段:(1)通过一个类的权限定名来获取此类的二进制字节流
                 (2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
                 (3)在java堆中生成一个代表这个类的class对象,作为方法区这些数据的入口。
验证阶段:文件格式(版本号、文件开头)、元数据(对字节码描述的信息语义分析)、字节码(类的方法体)、符号引用验证等。
准备阶段:正式为类变量分配内存并和设置变量初始值的阶段,都在方法区中分配。
解析阶段:虚拟机将常量池内的符号引用替换为直接引用的过程。类或接口的解析、字段的解析、类方法的解析、接口方法的解析。
初始化阶段:真正开始执行java程序中定义的代码。执行类构造器方法的过程。
jvm内存模型:Java代码是运行在Java虚拟机之上的,由Java虚拟机通过解释执行(解释器)或编译执行(即时编译器)来完成,故Java内存模型,也就是指Java虚拟机的运行时内存模型。

非共享数据区:
程序计数器PC:记录正在执行的虚拟机字节码的地址。
虚拟机栈:方法执行的内存区,每个方法执行时会在虚拟机栈中创建栈帧
本地方法栈:虚拟机的native方法执行的内存区。

共享数据区:
java堆:对象分配内存的区域
方法区:存放类的信息、常量、静态变量、编译器编译后的代码等数据;
常量池:存放编译器生成的各种字面量和符号引用,是方法区的一部分。



堆里面的分区:Edensurvival
from to
,老年代,各自的特点。
eden区:属于新生代,新建对象的内存分配地方,因为堆是内存共享的,所以分配地址需要加锁,为提高效率,就开辟一块内存由线程独享,叫tlab区域,在此区域不需要枷锁,所以,jvm给线程中的对象分配内存都在tlab区内分配。若tlab用完,依然在堆上分配,eden用完,会进行一次minor
gc。

survival from to区:也在新生代区,分为from和to区,当发生一次minor gc时,会相互互换,在发生minor gc时,eden和from区会将仍然存活的对象复制进to区,并清除内存,to区会把一些存活的足够旧的对象移到年老代。

年老代:存放存活时间久的,较大的对象,使用标记整理算法。当年老代已满时会进行一次major gc,回收年老代和年轻代中不再被使用的对象资源。

 对象创建方法,对象的内存分配,对象的访问定位
对象创建:
通过new关键字创建对象时,发生如下步骤操作:类加载检验、内存分配、并发处理、内存空间初始化、对象设置、执行init方法等。
类加载检查:jvm检查这个指令的参数是否能够在常量池中定位一个类的符号引用,并检查这个符号引用代表的类是否已被加载、解析和初始化过,如无,则进行累类的加载过程。
对象分配内存:
对象所需的内存在类加载完成后便完全确定,根据java堆中是否规整有两种分配方式(是否规则由垃圾收集器是否带有压缩整理功能决定)。
指针碰撞:java堆中内存完整,用过的内存放一边,空闲的放一边,中间放一个指针作为分界点的指示器,分配内存就是把指针向空闲一边移动所需内存大小相等距离。
空闲列表:堆中的内存不规整,已使用的和空闲的相互交错,虚拟机维护一张列表,记录内存的信息,在分配时从列表内找到一块足够大的空间划分给对象实例,并更新表。
并发处理:
对象的创建是非常频繁的行为,虚拟机采用CAS配上失败重试的方式保证更新操作的原子性。
本地线程分配缓冲tlab,只有tlab使用完再分配新的tlab时才加锁。
内存空间初始化:
虚拟机将分配到的内存空间都初始化为0值,使用tlab时在tlab内进行。保证了对象的实例字段在java代码中可以不赋初始值就直接使用,程序可以访问到这些字段的数据类型所对应的零值。
对象的设置:
虚拟机对对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的GC年代等信息。
执行init()
在上述步骤完成后,在jvm角度看已经创建了对象,但从java程序看,对象的创建才刚刚开始,init方法还未执行。一般来说执行完new操作后紧接着执行init方法,这样对象才创建完成。
对象的内存布局
分为对象头header、实例数据instance Data和对其填充Padding。
对象头:分为运行时数据和类型指针。运行时数据有gc信息、锁状态标志、哈希码等。类型指针即对象指向他的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
实例数据:是对象真正存储的有效信息,也是在程序代码中定义的各种类型的字段内容。无论是父类中继承还是子类中定义的。
对齐填充:对象的大小必须是8字节的整数倍,对象头正好是8字节的倍数,当实例数据没有对齐时,需要通过对齐来补充。
对象的访问定位:
java程序需要通过栈上的引用数据来操作堆上的具体对象。对象的访问方式取决于虚拟机实现,目前主流的有句柄访问和直接指针访问。
句柄访问:
句柄可以理解为指向指针的指针,维护指向对象的指针变化,而对象的句柄本身不发生变化;指针。指向对象,代表对象的内存地址。
在java堆中维护一个句柄池,引用中存放对象的句柄地址。在对象被移动时只会改变句柄的实例数据指针,而引用本身不需要改变。
直接指针访问:
java堆对象的布局中就必须考虑如何放置访问类型数据的相关信息,而引用中存放的直接就是对象地址。这样时间更快,节省了一次指针定位的时间开销。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

昵称 *