美文网首页
Java中一个对象是如何创建的?

Java中一个对象是如何创建的?

作者: ACtong | 来源:发表于2020-05-11 15:48 被阅读0次
1. 我们创建对象通常是使用的new关键字,但是虚拟机在内部又做了什么操作呢?

虚拟机在碰到new这个这个指令时,首先去检查这个参数能否在常量池中定位到一个类的符号引用,并且检查这个类是否被加载、解析和初始化。如果没有就要进行相关的类加载过程。

2. 类加载通过后,就对新生对象分配内存。
  • 新生对象所需的内存在类加载完成后便可以确定。这时候就会在堆中划分一份内存。
  • Java堆中的内存分配又有两种情况:
    [1] 堆中内存是完整的,分配过的内存放在一边,没有分配的放在另外一边,中间放一个指针作为分界点的指示器。一旦分配内存,指针就往空闲方向挪动一段对象相等的距离。这种方式又称为“指针碰撞”(Bump The Pointer)
    [2] 堆中内存是不完整的,已使用过的内存和空闲内存都混在一起。这时候虚拟机就要维护一个表,记录哪些内存是可以用的,用过后并且更新这张表。这种方式成为空闲列表(Free List)
  • 具体使用哪种分配方式,是由Java堆中的垃圾收集器是否带有空间压缩整理(Compact)能力来决定的。
    • 使用Serial、ParNew等带压缩 整理过程的收集器时,则采用“指针碰撞”(Bump The Pointer)
    • 使用CMS这种基于清除 (Sweep)算法的收集器时,则采用空闲列表(Free List)
3. 除了划分可用空间外,我们还需要考虑另外一个问题:
  • 虚拟机中对象的频繁创建,仅仅修改一个指针所指的位置,在并发是否线程安全的?
    例如,我们给A分配内存,指针还没来得及修改,对象B又同时使用了原来的指针来分配内存。在这样的情况下肯定是不安全的。
  • 那么我们又该如何的解决这种问题呢?
    [1] 采用CAS配上失败重试的方式,保证更新操作的原子性。
    [2] 把内存分配工作划分到不同的空间进行。即每个线程在Java堆中预先分配一块小内存,这又称为本地线程分配缓冲区(Thread Local Allocation Buffer,TLAB)。虚拟机是否使用TLAB,可以通过-XX:+/-UseTLAB参数来 设定。
4. 内存分配完成之后,我们就需要进行初始化值了。
  • 虚拟机将分配到内存的对象(但不包括对象头)进行初始零值。这就保证了对象的实例字段在Java代码中可以不赋初始值就可以使用了,并且访问到的是零值。
  • 这步如果使用了TLAB的话,初始化工作可以提前到TLAB分配时顺便进行。
5. 接下来虚拟机还要对对象进行必要的设置
  • 找到对象属于哪个类的实例
  • 找到类的元数据信息、对象的哈希码(实际上对象的哈希码会延后到真正调用Object::hashCode()方法时才计算)
  • 对象的GC分代信息
    以上信息都是属于对象头中的
  • 虚拟机根据当前的运行状态还会决定,是否启用偏向锁。
6. 以上工作是对象在虚拟机中已经完成了,但是Java程序中其实才刚刚开始

以上虚拟机中对象创建出来后,所有的字段都为默认的零值,对象需要的其他资源和状态信息也还没有按照预定的意图构造好。

  • new指令之后会接着执行Class文件中的<init>()方法,按照程序员的意愿对对象进行初始化

扩展内容:

类是如何加载的请参考:虚拟机的类加载机制
新生对象所需的内存在类加载完成后便可以确定,如何确定的请参考:对象的访问定位
对象头是什么?,请参考

相关文章

网友评论

      本文标题:Java中一个对象是如何创建的?

      本文链接:https://www.haomeiwen.com/subject/jwvjnhtx.html