对象的创建过程
- 虚拟机遇到new指令时,首先检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并检查这个符号是否已被加载解析和初始化过。如果没有,则必须要先执行相应的类加载的过程
- 检查通过后,虚拟机为新生对象分配内存。对象所需内存的大小在类加载完成后便可完全确定,为对象非配空间的任务等同于把一块确定大小的内存从Java堆中划分出来。
假如堆中的内存绝对规整,所有用过的内存放在一边,空闲的另一边,中间放着一个指针作为分界点的指示器,这种分配方式为指针碰撞。(Bump the Pointer)
如果堆中的内存并不规整,使用过和未使用过的相互交错,那么JVM需要维护一个列表记录那些可用那些不可用,这称为空闲列表。(Free List)
问题:并发情况下,出现正在给A对象分配内存,指针还没来得及修改,对象B又同时使用了原来的指针分配内存。
解决办法1:对分配空间的动作进行同步处理(原子性),实际上JVM采用CAS配上失败重试的方式保持原子性。
解决办法2:不同的线程分配在不同的空间,每个线程在Java堆中预先分配一小块内存。既TLAB(Thread Local Allocation Buffer)。哪个线程要分配内存,就在那个线程的TLAB上分配。当TLAB用完并分配TLAB时才需要同步锁定。
通过 -XX:+/-UseTLAB 设定使用TLAB
内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值。如果使用TLAB,这一工作过程也可以提前至TLAB分配时进行。这一步保证对象不赋值也可以使用,程序能访问到这些字段的数据类型对应的零值(0,flase等)
上面工作完成后,从虚拟机的视角来看,一个新的对象产生了,但从Java程序角度来看,对象才刚刚开始,方法还没执行,所有字段都还为0。