一、整体流程
类加载检查 ——〉分配内存——〉初始化零值——〉设置对象头——〉执行init方法
虚拟机遇到一条new指令时,首先会去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载、解析哈初始化过。如果没有,需要先执行相应的类加载过程。
在类加载通过后,虚拟机会为新生对象分配内存。对象所需内存的大小在类加载完成后便可以完全确定了。而分配内存的方式有两种:“指针碰撞”和“空闲列表”。“指针碰撞”要求Java堆中内存是绝对规整的,所有用过的内存都放在一边,空闲的内存放在另一边,中间会放置一个指针作为分界点的指示器。分配内存就是把指针向空闲空间那边挪动一段与对象想等大小的距离。“空闲列表”针对的是Java堆中不规整的内存,即使用的内存和空闲的内存相互交错,因此虚拟机需要维护一个列表,记录哪些内存可用。分配时,需要从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录。采用哪一种分配方式,取决于Java内存是否规整,java内存是否规整又取决于所采用的垃圾收集器是否带有压缩整理功能。Serial、ParNew等垃圾收集器带有压缩功能,而CMS这种需要使用空闲列表。
除此之外,在分配内存时还需要考虑到一个很重要的因素,即线程安全问题。虚拟机采用两种方案进行解决:一种是对分配内存空间的动作进行同步处理,即采用CAS加上失败重试方式保证更新操作的原子性;另一种是按照线程划分在不同空间中进行。