看完文章,如果觉得还可以,也请各位小伙伴点个“赞:+1:”,谢谢:pray:
《JVM从小白学成大佬》系列推出到现在,收到了很多小伙伴的好评,也收到了一些小伙伴的建议,在此表示感谢。
有几个小伙伴提出了希望出一篇介绍对象的创建及访问,猿人谷向来是没有原则的,小伙们要求啥,咱就尽力满足,毕竟文章就是对自己学习的一个总结及和各位小伙伴交流学习的机会。话不多说,直接开撸!
在Java程序运行过程中无时无刻都有对象被创建出来, java中对象可以采用new或反射或clone或反序列化的方法创建 。接下来我们我们介绍在虚拟机中,对象(限于普通Java对象,不包括数组和Class对象等)的创建过程。
字节码new表示创建对象,虚拟机遇到该指令时,从栈顶取得目标对象在常量池中的索引,接着定位到目标对象的类型。接下来,虚拟机将根据该类的状态,采取相应的内存分配技术,在内存中分配实例空间,并完成实例数据和对象头的初始化。这样,一个对象就在JVM中创建好了。
实例的创建过程,首先根据从类常量池中获取对象类型信息并验证类是否已被解析过,若确保该类 已被加载和正确解析 ,使用 快速分配 (fast allocation)技术为该类分配对象空间;若该类 尚未解析过 ,则只能通过 慢速分配 (slow allocation)方式分配实例对象。实例的创建流程如下图所示。
如果在实例分配之前已经完成了类型的解析,那么分配操作仅仅是在内存空间中划分可用内存,因此能以较高效率实现内存分配,这就是 快速分配 。
根据分配空间是来自于线程私有区域还是共享的堆空间,快速分配可以分为两种空间选择策略。HotSpot通过线程局部分配缓存技术(Thread-Local Allocation Buffers,即TLABs)可以在线程私有区域实现空间的分配。
可以通过VM选项UseTLAB来开启或关闭TLAB功能。
根据是否使用TLAB,快速分配方式有两种选择策略:
实例空间分配成功以后,将对实例进行初始化。待完成对象的空间分配和初始化后,就可以设置栈顶对象引用。当然, 对象的空间分配和初始化操作都是基于从类常量池中获取对象类型并确保该类已被加载和正确解析的前提下进行的 ,如果类未被解析,则需要进行慢速分配。
之所以成为慢速分配, 正是因为在分配实例前需要对类进行解析,确保类及依赖类已得到正确的解析和初始化 。慢速分配是调用InterpreterRuntime模块_new()进行的,实现代码如下。
// 确保要初始化的类不是抽象类型 klass->check_valid_for_instantiation(true, CHECK); // 确保类已初始化 klass->initialize(CHECK); // 分配实例 oop obj = klass->allocate_instance(CHECK); // 在线程栈中设置对象引用 thread->set_vm_result(obj);
建立对象是为了使用对象,Java程序需要通过栈上的reference数据来操作 堆 上的具体对象。由于reference类型在Java虚拟机规范中只规定了一个指向对象的引用,并没有定义这个引用应该通过何种方式去定位、访问堆中的对象的具体位置,所以对象访问方式也是取决于虚拟机实现而定的。
目前主流的访问方式有使用 句柄 和 直接指针 两种:
HotSpot就是使用第二种方式进行对象访问的,但从整个软件开发的范围来看,各种语言和框架使用句柄来访问的情况也十分常见。