HotSpot虚拟机
地址:https://blog.csdn.net/jin343229836/article/details/62047304
注:如其中有不懂的名词,下面有名词解释
1、对象的创建(限于普通Java对象,不包括数组和Class对象等)
(1)检查这个指令的参数能否在常量池中定位到一个类的符号引用,并检查这个符号引用代表的类是否被加载,解析,初始化,如果没有,先加载
(2)为新生对象在java堆中分配内存,java堆如果规整,分配内存的方法可使用“指针碰撞“,如果不规整,则使用”空闲列表“。选择哪种分配方式是根据这个虚拟机所采用的垃圾收集器是否带有压缩整理功能决定的。例如,在使Serial、 ParNew等带Compact过程的收集器时,系统采用的分配算法是指针碰撞,而使用CMS这种基于Mark-Sweep算法的收集器时,通常采用空闲列表。
(3)内存分配完之后,虚拟机需要将分配到的内存空间都初始化为零值。如果用TLAB,则在TLAB分配时进行。
(4)虚拟机对对象进行必要的设置,例如这个对象是哪个类的实例、 如何才能找到类的元数据信息、 对象的哈希码、 对象的GC分代年龄等信息。这些信息存放在对象的对象头(Object Header)之中。
(5)执行init方法,即按照程序员的意愿进行初始化。
注意事项:
对象创建在并发情况下不是线程安全的,解决办法:
(1)同步
(2)在对象创建时,把内存分配的动作按照线程划分在不同的空间中进行,即每个线程在java堆中预先分配一小块内存,称为本地线程分配缓冲(Thread Local Allocation Buffer TLAB),哪个线程要分配内存,在哪个线程的TLAB上分配,只有TLAB用完分配新的TLAB时,才需要同步锁定。
注:虚拟机是否使用TLAB,可以通过-XX:+/-UseTLAB参数来设定
2、在HotSpot虚拟机中,对象的内存中存储的布局可分为3块:对象头、实例数据、对齐填充
(1)对象头的数据长度在32位和64位的虚拟机上分别对应32bit和64bit。
(2)虚拟机中的分配策略:相同宽度的字段总是被分到一起。
3、对象的访问定位
HotSpot使用的是直接指针访问
图1句柄池访问对象
图2直接指针访问对象
直接指针访问要比句柄访问快一些,因为减少了一次指针定位的时间开销。
指针碰撞:
假设Java堆中内存是绝对规整的,所有用过的内存都放在一边,空闲的内存放在另一边,中间放着一个指针作为分界点的指示器,那所分配内存就仅仅是把那个指针向空闲空间那边挪动一段与对象大小相等的距离,这种分配方式称为“指针碰撞”(Bump the Pointer)。
空闲列表:
如果Java堆中的内存并不是规整的,已使用的内存和空闲的内存相互交错,那就没有办法简单地进行指针碰撞了,虚拟机就必须维护一个列表,记录上哪些内存块是可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录,这种分配方式称为“空闲列表”(Free List)
符号引用:
符号引用是一个字符串,它给出了被引用的内容的名字并且可能会包含一些其他关于这个被引用项的信息——这些信息必须足以唯一的识别一个类、字段、方法。这样,对于其他类的符号引用必须给出类的全名。对于其他类的字段,必须给出类名、字段名以及字段描述符。对于其他类的方法的引用必须给出类名、方法名以及方法的描述符。