JVM中一个对象的产生过程
在一个java程序中创建一个对象仅仅是使用一个new关键字,在给构造函数必要的参数好了,但是在JVM中发生的事远远不止于此,下面我们就来探索一下JVM中一个对象的创建需要经历哪些过程.分析如下图
1.信息检查阶段
在JVM遇到new关键字的时候,首先检查是否可以在方法区的常量池中定位到一个符号引用(既检查是不是有满足参数的函数),然后检查这个符号代表的类是否已经被加载过了(查看是不是有这样一个类).
2.内存分配
当对对象进行必要的检查之后就需要对该对象分配空间,因为一个类的对象所需要的内存空间大小是固定的.这里有两种分配内存的方式.
- 假设java堆中的内存是完全规整的,所有被使用的内存放在一边,未被使用的内存在另一边.中间是一个指针来作为分界点.那么分配内存只是将指针移动对象内存大小的区域即可,这种方法被称为"指针碰撞(Bump The Pointer)".
- 但是如果java堆中内存空间不是规整的.使用过的内存和未使用的内存交错在一起,那么就不能简单的使用指针碰撞了.而是需要维护一个列表,这个列表需要记录哪些内存区域是可用的,哪些是不可以用的.在分配的时候将一块大小相等的内存空间分配给对象并记录.这种方法被称为"空闲列表(Free List)".
但是,除了划分内存空间外,我们还需要考虑另外一个问题:对象创建在JVM中是非常频繁的行为,即使仅仅修改一个指针所指向的位置,在高并发的情况下也不是安全的(JVM正在为对象a分配内存,指针还没来得及修改,对象b同时使用原来的指针来分配内存).为了解决这种问题,我们也有两种方案.
- 对分配内存动作进行同步处理(实际上是采用CAS)
- 把内存分配的动作按照线程划分在不同的空间中执行.既每个线程在Java堆中预先分配一块内存,线程在分配的内存空间充足的情况下,在自己的内存内分配内存.
3.初始化
在内存分配好之后我们就需要对对象进行一些基本的初始化.首先初步了解下一个堆中的对象分为几个部分.
对象头(Header),实例数据(Instance Data),对齐补充(Padding).
对象头中主要包括的是这个对象的基本信息(如哈希码,GC分带年龄等),实例数据存储就是真正的有效信息,比如这个对象的字段值,对齐补充没有什么实际含义.
所以初始化的第一步: 就是把不包对象头的内存空间全都初始化为零.这样就保证了对象的实例字段在java中不赋初始值也能使用.第二步就是把对象头进行一些必要的设置.
4.构造
这个阶段就是就是将对象按照程序员的意愿构造出来.