Jvm-对象探秘

  • 1.对象加载方式

  (《深入理解Java虚拟机:JVM高级特性与最佳实践》笔记)

    1.对象的创建:当虚拟机接受一条new对象的指令时(限于java对象,不包括数组,Class对象等...)会经历如下步骤:

      (1)检查这个指令的参数是否在常量池(运行时常量池)中定义到一个类符号的引用。

      (2)检查这个符号代表的类时候被加载、解析或者初始化过(类的加载流程),否则会通过classLoader尝试加载

      (3)在类的加载检查通过后,尝试为新对象分配内存空间。由于类的大小可以在类加载完成后完全确定,所以为对象划分空间,就相当于把一块固定大小的内存容量从本地内存(1.8使用的是本地内存而不是jvm的内存)中划分出来。那么就会因为GC是否拥有压缩整理算法而产生不同的分类方式

      1.指针碰撞:如果java中的堆内存空间是整齐的,所有用过的内存放在一边,没有用过的也放在一边,中间使用一个指针作为临界点的指示器。这样分配内存就相当于移动指针和对象相同的大小距离。

      2.空闲列表:如果java中的堆内存空间不是整齐的,用过的和未用过的内存空间相互交错!那么就不能使用指针碰撞的方式了,虚拟机就必须维护一个空闲列表,记录那些空间是可用的以及大小,为分配时从列表中找到足够大的空间,分配给对象,并更新表信息。

   

2.当为对象分配内存时,可能出现不安全的情况:

      在分配内存时,还可能出现不安全的情况,比如:当前正在给对象A分配内存空间,指针还来来得及修改时,对象B同时又用原来的指针分配内存。为了避免不安全的情况,主要有两种解决方式:

      1.对分配内存的过程增加同步处理:实际上虚拟机采用了CAS和失败重建的方法保证分配内存空间的原子性。

      2.将内存分配按照不同的线程划分到不同的空间中,也就是说,可以在java中的每一个线程上预先设置一定的空间(本地线程分配缓冲TLAB)。那个线程要分配空间时,就在那个线程上面分配,只有当TLAB分配的空间分配完了以后,需要分配新的内存时,才加上同步锁定。 但是,虚拟机是否需要使用TLAB,需要通过--XX:+UserTLAB 参数来设置。

 3.对象初始化:

     当内存分配完成后,虚拟机会对分配到的内存空间进行初始化(不包括对象头),如果使用TLAB,这一过程也可以提前至TLAB中进行。则一步就保证了对象的实例字段,在Java代码中,不需要赋初始值就可以直接使用,程序能够访问到这些字段所对应的初始值。

    最后,jvm执行对象的构造方法,按照程序员的意愿去初始化它,这样一个可用的对象就创建出来了。

 

2.对象的内存布局

   在hotSpot虚拟机中,对象在内存中的存储结构分为三部分:对象头(Header),实例数据(Instance Data),对齐填充(Padding)  

  1. 对象头被分为了两部分:  

      Part1:用于存储对象自身的运行时数据(Mark Word 工作状态),比如哈希码,GC分代年龄,锁状态标示,线程持有的锁等。该部分的长度在32位和64位虚拟机中,分别占位32字节和64字节。为了让Mark Word存储更多的数据,该部分使用了不固定长度的数据结构。  

      Part2:类型指针:用于存储对象指向它类元信息的指针,通过该指针可以判断对象是属于哪一个类的实例。

  2。实例数据:真正存储对象的有效信息,也就是说在程序代码中所定义的各种类型的字段内容。无论是本身的还是从父类继承来的,都会记录下来。

  3.对齐填充:因为HotSpot的自动内存管理要求对象的起始地址必须是8直接的整数倍,也就是说对象的大小必须是8直接的整数倍。而对象头的大小一般是8字节的一倍或者两倍,但实例数据部分没有对齐时,就需要对齐填充来补全。

 

  

 

    

posted @ 2019-03-17 21:17  刘飞飞飞  阅读(151)  评论(0编辑  收藏  举报