创建对象:
虚拟机遇到一条字节码new指令时,先去检查这个指令的参数是否能在常量池中定位到一个类的符号引用;
并且检查这个符号引用代表的类是否已被加载、解析和初始化过。如果没有,必须先执行相应的类加载过程。
类加载通过后,虚拟机将为新生对象分配内存。
分配内存:两种分配方式:
(1)指针碰撞:
java堆中的内存绝对规整。
所有使用过的内存放在一边,所有未使用过的内存放在另一边;中间放一个指针作为分界点的指示器。
分配内存就是将指针向空闲空间方向挪动一段与对象大小相等的距离。
(2)空闲列表:
java堆中的内存不规整。
已被使用的内存和空闲内存交互在一起。
虚拟机会维护一个列表,记录哪些内存是可用的。分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录。
java堆是否规整:
由所采用的垃圾收集器是否带有空间压缩整理的能力决定。
采用指针碰撞:
Serial、ParNew等带压缩整理过程的收集器。
采用空闲列表:
使用CMS这种清除算法的收集器时。
需要考虑的问题:
可能出现的情况:
当正在给对象A分配内存,指针还没来得及修改,对象B又同时使用了原来的指针来分配内存的情况。
解决方案:
(1)对分配内存空间的动作进行同步处理
虚拟机是采用CAS配上失重重试的方式保证更新操作的原子性。
(2)把内存分配的动作按照线程划分在不同的空间进行
每个线程在java堆中预先分配一下块内存,称为本地线程分配缓冲(TLAB=Thread Local Allocation Buffer)。
哪个线程要分配内存,就在哪个线程的本地缓冲区中分配,只有本地缓冲区用完了,分配新的缓存区时才需要同步锁定。
虚拟机是否需要使用TLAB,可以通过-XX:+/-UserTLAB参数来设定。
内存分配完成之后,虚拟机必须将分配到的内存空间(不包括对象头)都初始化为零值。
如果使用了TLAB,这一项工作也可以提前至TLAB分配时顺便进行。
这步操作保证了对象的实例字段在java代码中可以不赋初始值就直接使用,使程序能访问到这些字段的数据类型所对应的零值。