Java对象的创建

  • Post author:
  • Post category:java


谢谢平台提供-http://bjbsair.com/2020-04-13/tech-info/65247.html

Java对象的创建过程,从虚拟机接收到字节码new指令开始。



1 检查阶段

​ 虚拟机遇到new指令之后,会根据new指令的参数去常量池中

定位类的符号引用

,并且检查这个符号引用代表的类,

是否已经加载、解析、初始化

,如果没有,则需要先执行相应的类加载过程。

​ 如果类加载检查通过了,虚拟机则会为新对象分配内存,具体分配多少,在类加载完成后就能确定。



2 内存分配

​ 为对象分配内存的过程,就相当于把一块确定大小的内存,从Java堆中划分出来,这个划分的方式,主要有两种:一是

指针碰撞

,二是

空闲列表

,两种方式有不同的应用场景。



2.1 指针碰撞

​ 指针碰撞适用于绝对规整的Java堆内存,即Java堆内存中,被使用过的内存在一边,空闲的内存在另外一边,以一个指针作为分界,分配内存的过程就是指针向空闲的一边移动固定大小的过程,简单而高效。

Java对象的创建



2.2 空闲列表

​ Java堆中的内存,很可能不是绝对规整,而是使用过的内存和空闲内存互相交错存在的。这种情况,为对象分配内存就可以使用空闲列表的方式。虚拟机将堆中哪些内存是空闲的记录在一个列表中,在为对象分配内存的时,从列表中记录的内存中找到足够大的一块划分给新对象,并更新列表中的记录。

Java对象的创建



2.3 内存分配方式的依据

​ 具体使用哪种方式为新对象分配内存取决于Java堆内存是否规整,而Java堆内存是否规整,取决于采用的垃圾收集器是否具有

空间压缩整理

的能力。当使用具有

空间压缩整理

能力的垃圾收集器时(如

Serial,ParNew

等),虚拟机使用指针碰撞的方式为对象分配内存,而当使用

CMS

这种基于

清除算法

的收集器时,理论上就只能采用空闲列表的方式为对象分配内存。



2.4 内存分配的问题和解决方案

​ 在Java应用中,对象的创建非常频繁,对应Java虚拟机为新对象分配内存的行为也非常频繁,而虚拟机为对象分配内存的操作在并发时并不是线程安全的,因为分配和移动指针、分配内存和修改空闲空间列表都不是原子性操作,很可能多个对象的内存分配是基于指针指向的同一个位置或者多个对象被分配到了同一块空闲空间。解决这种问题有两种方案:

  1. 对给对象分配空间的操作进行同步处理,保证同一时间只能对一个对象分配内存
  2. 预先为每个线程在Java堆中分配独立的空间,也就是

    本地线程分配缓冲

    。这样每个线程为对象分配内存都是在各自的线程缓冲区中进行,也就不会有线程安全问题了。但是,缓冲区用完时,重新分配缓冲区的操作还是要进行同步锁定的。虚拟机通过

    -XX:+/-UseTLAB

    设定是否使用

    本地线程分配缓冲



3 将内存初始化为零值

​ 内存分配完成之后,虚拟机还必须将分配到的对象头之外的空间都置为零值(如果使用了TLAB,也可以在分配TLAB时置零),这样可以保证对象的属性字段不赋初始值就可以使用,此时程序访问这些字段将得到字段类型对应的零值( byte,short,int,long对应的是 0,float和double对应的是 0.0,boolean对应的是 false,char对应的是 \u0000,引用类型对应的也是null )



4 设置对象头信息

根据是否使用偏向锁,设置对象头信息,如对象是哪个类的实例、对象的hash码、对象的GC分代年龄等谢谢平台提供-http://bjbsair.com/2020-04-13/tech-info/65247.html

Java对象的创建过程,从虚拟机接收到字节码new指令开始。



1 检查阶段

​ 虚拟机遇到new指令之后,会根据new指令的参数去常量池中

定位类的符号引用

,并且检查这个符号引用代表的类,

是否已经加载、解析、初始化

,如果没有,则需要先执行相应的类加载过程。

​ 如果类加载检查通过了,虚拟机则会为新对象分配内存,具体分配多少,在类加载完成后就能确定。



2 内存分配

​ 为对象分配内存的过程,就相当于把一块确定大小的内存,从Java堆中划分出来,这个划分的方式,主要有两种:一是

指针碰撞

,二是

空闲列表

,两种方式有不同的应用场景。



2.1 指针碰撞

​ 指针碰撞适用于绝对规整的Java堆内存,即Java堆内存中,被使用过的内存在一边,空闲的内存在另外一边,以一个指针作为分界,分配内存的过程就是指针向空闲的一边移动固定大小的过程,简单而高效。

Java对象的创建



2.2 空闲列表

​ Java堆中的内存,很可能不是绝对规整,而是使用过的内存和空闲内存互相交错存在的。这种情况,为对象分配内存就可以使用空闲列表的方式。虚拟机将堆中哪些内存是空闲的记录在一个列表中,在为对象分配内存的时,从列表中记录的内存中找到足够大的一块划分给新对象,并更新列表中的记录。

Java对象的创建



2.3 内存分配方式的依据

​ 具体使用哪种方式为新对象分配内存取决于Java堆内存是否规整,而Java堆内存是否规整,取决于采用的垃圾收集器是否具有

空间压缩整理

的能力。当使用具有

空间压缩整理

能力的垃圾收集器时(如

Serial,ParNew

等),虚拟机使用指针碰撞的方式为对象分配内存,而当使用

CMS

这种基于

清除算法

的收集器时,理论上就只能采用空闲列表的方式为对象分配内存。



2.4 内存分配的问题和解决方案

​ 在Java应用中,对象的创建非常频繁,对应Java虚拟机为新对象分配内存的行为也非常频繁,而虚拟机为对象分配内存的操作在并发时并不是线程安全的,因为分配和移动指针、分配内存和修改空闲空间列表都不是原子性操作,很可能多个对象的内存分配是基于指针指向的同一个位置或者多个对象被分配到了同一块空闲空间。解决这种问题有两种方案:

  1. 对给对象分配空间的操作进行同步处理,保证同一时间只能对一个对象分配内存
  2. 预先为每个线程在Java堆中分配独立的空间,也就是

    本地线程分配缓冲

    。这样每个线程为对象分配内存都是在各自的线程缓冲区中进行,也就不会有线程安全问题了。但是,缓冲区用完时,重新分配缓冲区的操作还是要进行同步锁定的。虚拟机通过

    -XX:+/-UseTLAB

    设定是否使用

    本地线程分配缓冲



3 将内存初始化为零值

​ 内存分配完成之后,虚拟机还必须将分配到的对象头之外的空间都置为零值(如果使用了TLAB,也可以在分配TLAB时置零),这样可以保证对象的属性字段不赋初始值就可以使用,此时程序访问这些字段将得到字段类型对应的零值( byte,short,int,long对应的是 0,float和double对应的是 0.0,boolean对应的是 false,char对应的是 \u0000,引用类型对应的也是null )



4 设置对象头信息

根据是否使用偏向锁,设置对象头信息,如对象是哪个类的实例、对象的hash码、对象的GC分代年龄等谢谢平台提供-http://bjbsair.com/2020-04-13/tech-info/65247.html

Java对象的创建过程,从虚拟机接收到字节码new指令开始。



1 检查阶段

​ 虚拟机遇到new指令之后,会根据new指令的参数去常量池中

定位类的符号引用

,并且检查这个符号引用代表的类,

是否已经加载、解析、初始化

,如果没有,则需要先执行相应的类加载过程。

​ 如果类加载检查通过了,虚拟机则会为新对象分配内存,具体分配多少,在类加载完成后就能确定。



2 内存分配

​ 为对象分配内存的过程,就相当于把一块确定大小的内存,从Java堆中划分出来,这个划分的方式,主要有两种:一是

指针碰撞

,二是

空闲列表

,两种方式有不同的应用场景。



2.1 指针碰撞

​ 指针碰撞适用于绝对规整的Java堆内存,即Java堆内存中,被使用过的内存在一边,空闲的内存在另外一边,以一个指针作为分界,分配内存的过程就是指针向空闲的一边移动固定大小的过程,简单而高效。

Java对象的创建



2.2 空闲列表

​ Java堆中的内存,很可能不是绝对规整,而是使用过的内存和空闲内存互相交错存在的。这种情况,为对象分配内存就可以使用空闲列表的方式。虚拟机将堆中哪些内存是空闲的记录在一个列表中,在为对象分配内存的时,从列表中记录的内存中找到足够大的一块划分给新对象,并更新列表中的记录。

Java对象的创建



2.3 内存分配方式的依据

​ 具体使用哪种方式为新对象分配内存取决于Java堆内存是否规整,而Java堆内存是否规整,取决于采用的垃圾收集器是否具有

空间压缩整理

的能力。当使用具有

空间压缩整理

能力的垃圾收集器时(如

Serial,ParNew

等),虚拟机使用指针碰撞的方式为对象分配内存,而当使用

CMS

这种基于

清除算法

的收集器时,理论上就只能采用空闲列表的方式为对象分配内存。



2.4 内存分配的问题和解决方案

​ 在Java应用中,对象的创建非常频繁,对应Java虚拟机为新对象分配内存的行为也非常频繁,而虚拟机为对象分配内存的操作在并发时并不是线程安全的,因为分配和移动指针、分配内存和修改空闲空间列表都不是原子性操作,很可能多个对象的内存分配是基于指针指向的同一个位置或者多个对象被分配到了同一块空闲空间。解决这种问题有两种方案:

  1. 对给对象分配空间的操作进行同步处理,保证同一时间只能对一个对象分配内存
  2. 预先为每个线程在Java堆中分配独立的空间,也就是

    本地线程分配缓冲

    。这样每个线程为对象分配内存都是在各自的线程缓冲区中进行,也就不会有线程安全问题了。但是,缓冲区用完时,重新分配缓冲区的操作还是要进行同步锁定的。虚拟机通过

    -XX:+/-UseTLAB

    设定是否使用

    本地线程分配缓冲



3 将内存初始化为零值

​ 内存分配完成之后,虚拟机还必须将分配到的对象头之外的空间都置为零值(如果使用了TLAB,也可以在分配TLAB时置零),这样可以保证对象的属性字段不赋初始值就可以使用,此时程序访问这些字段将得到字段类型对应的零值( byte,short,int,long对应的是 0,float和double对应的是 0.0,boolean对应的是 false,char对应的是 \u0000,引用类型对应的也是null )



4 设置对象头信息

根据是否使用偏向锁,设置对象头信息,如对象是哪个类的实例、对象的hash码、对象的GC分代年龄等