前言
当new关键字找到了需要实例化对象的类信息,那么就可以开始在内存中分配对象实例了。(此处就开始涉及Java内存结构了)
0x01 内存分配
通过new关键字每次我们都创建了该类的一个新对象实例,既然要创建对象实例,就要有个存放对象实例的地方。所以需要在内存中分配一块空间给这个对象实例
问题:分配在内存的哪里?
分配在Java内存结构中的堆区
问题:如何分配?
分配方式根据内存是否规整决定,若内存规整则使用指针碰撞,若内存不规整则使用空闲列表。
指针碰撞:将当前内存指针指向的地址下移该对象需要的内存大小
空闲列表:维护内存使用情况,挑选出大小足够分配该对象的内存(若没有足够的内存就会触发垃圾回收,其实是minor gc,但可能触发full gc)
内存是否规整根据Java垃圾收集器是否带有压缩整理功能决定
此处就涉及到了Java内存结构、垃圾收集算法(如何回收垃圾)、垃圾收集器
总体流程:加载类信息-》内存分配-》对象实例信息初始化(init方法,即构造器)
0x02 线程安全问题
问题:为什么会有线程安全问题?
对象创建非常频繁,在并发情况下不是线程安全。比如分配了内存,但是指针还没有移动或者空闲列表没有调整。
为什么线程不安全?因为Java内存模型的方式采用的是共享内存方式(后续文章)
解决方式一:采用CAS配上失败重试方式保证更新操作的原子性
解决方式二:TLAB方式:每个线程在堆中预先分配一段内存,用完就重新分配,只有TLAB用完并重新分配才会加锁
0x03 对象的内存布局
对象在内存中存储的布局:对象头、实例数据、对齐填充
对象头:存储对象自身的运行时数据(如哈希码、GC分代年龄、锁状态标志等)+类型指针(通过该指针确定这个对象是哪个类实例)
实例数据:对象真正有意义的数据
对齐填充:不是必然存在的,没特别含义
0x04 对象的访问定位
使用句柄:栈指针指向堆中的一个句柄池,句柄池维护指向对象实例的指针
直接引用:栈指针直接指向堆中的对象实例
0xFF 总结
对象分配其实就非常简单,找到对应的类信息,分配内存,设置一些jvm规定的信息。但是不同jvm虚拟机的实现不同,所以会出现不同的机制去实现。在这一阶段中。我们需要了解java内存结构,因为对象实例的分配需要在内存中,java内存结构方便jvm更好的管理内存
(分配内存、回收内存)。因此也引出了垃圾收集算法和垃圾收集器(垃圾收集算法的集体实现)