Java对象创建的过程,总共来说分为五部分;

类加载过程
当虚拟机遇到一条new指令时,它将首先检查该指令的参数是否可以在常量池中找到该类的符号引用,并检查该符号引用表示的类是否已被加载,解析和初始化。。如果不是,则必须首先执行相应的类加载过程。
分配内存
通过类加载检查后,虚拟机将为新对象分配内存。可以在加载类之后确定对象所需的内存大小。为对象分配空间的任务等效于从Java堆中分配一定大小的内存。有两种分配方法:“指针冲突”和“空闲列表”。分配方法的选择取决于Java堆是否正常,而Java堆是否正常取决于所使用的垃圾回收器是否具有压缩功能。
指针碰撞:
场景:Java堆中内存是绝对规整的;
原理:所有用过的内存都放在一边,空闲的内存放在另外一边,中间放一个指针作为分界点的指示器,分配内存时只需要把那个指针向空闲空间那边挪动一段与对象大小相等的距离就可以了;
GC收集器:Serial、ParNew等带Compact过程的收集器。
空闲列表:
场景:Java堆中内存不是规整的;
原理:虚拟机会维护一个列表,记录上哪些内存块是可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录;
GC收集器:CMS基于Mark-Sweep算法的收集器。
内存分配并发的问题
创建对象时需要考虑的问题是,在并发情况下线程是否安全。因为在虚拟机中对象创建是一种非常常见的行为,所以可能会发生以下情况:正在将内存分配给对象A,指针没有时间修改,而对象B同时使用原始指针分配内存。因此必须要保证线程安全,解决这个问题有两种方案:
**CAS以及失败重试(比较和交换机制):**对分配内存空间的操作进行同步处理——实际上虚拟机采用CAS配上失败重试的方式保证更新操作的原子性。CAS操作需要输入两个数值,一个旧值(操作前期望的值)和一个新值,在操作期间先比较旧值有没有发送变化,如果没有变化,才交换成新值,否则不进行交换。
**TLAB(分配缓冲):**内存分配操作根据线程划分为不同的空间,也就是说,每个线程在Java堆中预分配一小块私有内存,这是本地线程分配缓冲区。TLAB的目的是允许每个Java应用程序线程在为新对象分配内存空间时使用其自己的专用分配指针来分配空间,从而减少同步开销。
初始化零值
内存分配完成后,虚拟机需要将分配的内存空间初始化为零(不包括对象标头)。此步骤确保对象的实例字段可以直接在Java代码中使用,而无需分配初始值。程序可以访问与这些字段的数据类型相对应的零值。
设置对象头
初始化零值后,虚拟机需要设置必要的对象,例如,对象是该类的实例,如何查找该类的元数据信息,对象的哈希值以及该对象的GC生成年龄。目的。此信息存储在对象标题中。另外,根据虚拟机的当前运行状态(例如是否启用偏置锁定等),对象标头将具有不同的设置方法。
执行Init方法
在上面工作都完成之后,从虚拟机的视角来看,一个新的对象已经产生了,但从Java程序的视角来看,对象创建才刚开始,方法还没有执行,所有的字段都还为零。所以一般来说,执行new指令之后会接着执行方法,把对象按照程序员的意愿进行初始化,这样一个真正可用的对象才算完全产生出来。
推荐阅读:jvm培训:如何判断哪些对象需要回收?
如果你想了解更多关于java架构师的专业知识,可以加入JAVA架构师交流群:1037935907,里面都是同行,有资源分享包括但不限于(分布式架构、高可扩展、高性能、高并 发、Jvm性能调优、Spring,MyBatis,Nginx源码分析,Redis,ActiveMQ、、Mycat、Netty、Kafka、Mysql 、Zookeeper、Tomcat、Docker、Dubbo、Nginx)。欢迎一到五年的工程师加入,合理利用自己每一分每一秒的时间来学习提升自己,不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代!

355

被折叠的 条评论
为什么被折叠?



