分别从 jvm 层次和程序视角来看:
jvm 层次来看的话, 当 new 对象的时候, jvm 先会到常量池中查找对象
jvm 层次:
jvm 虚拟机遇到一条 new 指令的时候, 首先会到常量池中去查找当前的指令的参数是否已经加载解析过, 如果没有就要进行类的加载 (类加载单独在说),
如果已经加载, 这个时候就会到堆内存中给当前的类分配内存, 类的内存大小在类加载完成之后就可以确定. 如果说堆内存是绝对规则的, 已经使用的内存和
未使用的内存中间就可以使用指针进行区分, 只要指针向未使用的内存区域移动指定的类的大小的内存空间即可. 如果说堆的内存不是规则, 已使用的和未使用的内存交
互存在, 这个时候就没法使用指针碰撞了, 必须维护一个堆内存的使用列表, 记录那些是使用的那些是未使用的, 在分配的内存的时候给新的对象分配一个足够大的
未使用的内存, 这种方式叫做'空闲列表'.
对于 jvm 使用哪种分配方式, 就要看堆内存的空间是否完整, 堆内存是否规则, 就要看 jvm 使用了哪种垃圾回收器 (垃圾回收器是否带有压缩整理功能). 例如使用 Serial,
ParNew 等带 Compact 过程的收集器, 使用的就是指针碰撞, 而使用 CMS 这种基于 Mark-Sweep 算法的收集器, 就是使用空闲列表.
对象的创建对应 jvm 来说也是非常频繁的, 即使是修改一个指针的位置, 在高并发的情况下也是不安全的, 有可能在在给 A 分配内存, 指针还没有移动,
JVM 又使用了原先的指针给 B 分配了这块内存. 解决这个问题有两种方案, 一种是对分配内存的动作进行同步操作, 实际上 jvm 采用的是 CAS 配上失败重试. 另一种方式就是
按照线程在不同的空间进行, 既每个线程预先在堆中分配一块内存, 也就是叫 TLAB(本地线程分配缓存), 那个线程需要分配内存, 就是使用哪个对象的 TLAB, 除非线程的 TLAB 不足,
才会去同步申请内存, 是否使用 TLAB, 可以通过使用 --XX:+/-UseTLAB 参数来设定.
接下来就是对对象进行必要的设置, 例如对象头的设置 (这个对象时哪个类的实例, 怎么才能找到这个类的元数据信息, 对象的哈希码等)
以上完成之后, jvm 层面的来说, 一个新的对象已经创建成功, 对应程序而言, 对象才创建才刚刚开始.
程序方面的对象创建:
程序方面就是按照个人的设置进行初始化对象, 以图表的展示初始化的一个过程
运行结果:
来源: http://www.bubuko.com/infodetail-2985963.html