Java是一门面向对象的编程语言,在语言层面上,创建对象通常只需要一个new关键字,而在虚拟机中,对象(普通Java对象)的创建又是另外一个过程。
接收new指令
虚拟机遇到一条new指令时,将去检查这个指令的参数是否能在常量池中定位到一个类符号,并且检查这个符号引用代表的类是否已经被加载、解析和初始化。若没有,将会执行相应的类加载过程(在以后的文章中将讲到)。
为对象分配内存
对象需要的内存大小在类加载完成后便可以完全确定,为对象分配内存的任务实际上是从Java堆中划分出一块确定大小的内存。分配方式:
- 指针碰撞 (Bump the Pointer): 假设Java堆中内存是规整的,所有用过的内存都放在一边,空闲的内存在另一边,中间有一个指针作为分届点指示器,分配内存时,仅仅需要把指针向空闲区域挪动一段与对象大小相等的距离。
- 空闲列表 (Free List): 如果Java堆中的内存不是规整的,已使用的内存和空闲内存交错在一起,那就没有办法通过指针碰撞的方式来进行内存分配了,这时候虚拟机需要维护一个列表,记录哪些内存是可用的,在分配内存时,从列表中找到一块足够大的空间划分给对象实例,并且更新列表记录。
选择那种分配方式是由Java堆是否规整决定的,而Java堆是否规整又是由所采用的垃圾收集器是否带有压缩整理功能决定的。
内存分配的线程安全
在并发的情况下,可能出现正在给A分配内存时,指针还没有来得及修改,对象B又同时使用了原来的指针来分配内存。解决办法有两种:
- 对分配的内存进行同步处理——实际上虚拟机采用CAS加上失败重试的方式保证更新操作具备原子性。
- 把内存分配的动作按照线程划分在不同的空间之中进行,就是每个线程在Java堆中预先分配一小块内存,称为本地线程分配缓冲区(Thread Local Allocation Buffer, TLAB),哪个线程要分配内存就在哪个线程的TLAB上分配,只有TLAB用完并分配新的TLAB时,才需要同步锁定。虚拟机是否使用TLAB,采用
-XX:+/-UseTLAB
参数来设定
内存初始化
内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头),如果使用了TLAB,这一工作可以提前到TLAB分配时进行。
这一操作保证了对象实例字段在Java代码中可以不赋值就直接使用,程序访问到这些字段的数据类型所对应的零值。
设置对象
虚拟机需要对存放在对象头中的 例如对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的GC分代年龄等信息进行必要的设置。
在上面的工作完成之后,从虚拟机的角度,一个新的对象已经产生,但从Java程序的角度,对象创建才刚刚开始。
网友评论