文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Java 对象到底是如何创建的?类加载机制是什么?对象的内存布局和访问方式有哪些?

2024-11-28 13:31

关注

在 Java 中,对象的创建过程离不开类的加载与初始化,因此理解类加载的原理和对象的内存布局,是掌握 JVM 性能优化的关键。

本章基于类加载机制的深入解析,将详细讲解对象的创建、内存布局、访问方式及分配策略,帮助你从理论到实践全面掌握 JVM 对象管理的底层逻辑。

类加载机制概述

类加载是 Java 对象创建的基础。

JVM 通过类加载器将 .class 文件中的二进制数据加载到内存,并将其转化为 JVM 可以识别的运行时数据结构。以下是类加载的核心步骤:

类加载的七个阶段

根据《Java 虚拟机规范》,类加载分为七个阶段:

  1. 加载 (Loading) :将 .class 文件的二进制数据加载到内存,生成 Class 对象。
  2. 验证 (Verification) :校验 .class 文件的格式和内容是否符合规范,确保安全性。
  3. 准备 (Preparation) :为静态变量分配内存并初始化默认值。
  4. 解析 (Resolution) :将符号引用替换为直接引用。
  5. 初始化 (Initialization) :执行静态变量的赋值及静态代码块。
  6. 使用 (Using) :通过程序调用类的静态变量或方法。
  7. 卸载 (Unloading) :释放类占用的内存资源。

根据 《Java 虚拟机规范》 中的规定,类加载可以分为七个阶段,分别为 加载 (Loading)、验证 (Verification)、准备 (Preparation)、解析 (Resolution)、初始化 (Initialization)、使用 (Using) 和 卸载 (Unloading),其中 验证、准备 和 解析 三个阶段整体又称为 链接 (Linking)。

图片图片

类加载就像从蓝图设计到建筑施工的过程:

加载阶段主要是使用 "类加载器" 将本地或者远程网络中的字节码文件,通过读字节流的方式加载到 Java 虚拟机内存中。在加载阶段中 Java 虚拟机主要完成以下三件事情:

其中常用的类加载器有三种,分别是:

类加载器

描述

引导类加载器 BootstrapClassLoader

引导类加载器是使用 C++ 语言实现的,用于加载 Java 中的核心类库的,一般会加载 JAVA_HOME 目录下的 /jre/lib 文件夹下的 jar 和配置。


扩展类加载器 ExtClassLoader

扩展类加载器主要负责加载 Java 的扩展类库,一般会加载 JAVA_HOME 目录下的 /jre/lib/ext 文件夹下的 jar。

应用类加载器 AppClassLoader

应用类加载器是应用程序中默认的类加载器,可以加载 CLASSPATH 变量指定目录下的 jar,并且一般情况下,我们编写的 Java 应用的类,都是使用该类加载器完成加载的。

对象的内存分配与初始化

当类加载完成后,JVM 开始为新对象分配内存并完成初始化。

对象内存分配

确定分配区域

分配方式

对象初始化流程

  1. JVM 将分配的内存清零(不包括对象头)。
  2. 调用对象的构造方法 ,完成实例变量初始化。

对象的内存布局

Java 对象在内存中的布局分为三部分:对象头、实例数据 和 对齐填充。

图片图片

对象头

对象头包含以下内容:

  1. Mark Word ,存储对象的哈希码、GC 状态、锁标志等运行时信息。
  2. Class Pointer ,指向对象的类元信息,用于确定对象类型。
  3. 数组长度(仅数组对象) ,数组对象会额外存储数组长度信息。

对象头结构示意图

图片图片

对象访问方式

JVM 提供了两种对象访问模式:句柄池 和 直接指针。

句柄池

句柄:如果使用句柄访问对象,JAVA 堆中将会划分一块内存作为句柄池,reference 中存储的就是对象的句柄地址,句柄中包含对象实例数据与类型数据。

图片图片

优点:对象内存地址变化时,只需更新句柄,而无需修改引用。

直接指针

如果使用直接指针访问,则 reference 存储对象地址。优点:访问速度快,少了一次间接访问。

图片图片

对象内存分配策略

JVM 的内存分配策略与垃圾回收机制密切相关。以下是常见的内存分配方式:

  1. 栈上分配:通过逃逸分析,JVM 可将生命周期短的对象分配在栈上,避免 GC 的参与。
  2. 新生代与老年代分配:新生代分配,默认分配在 Eden 区;Survivor 区用于存活对象的复制和晋升。生命周期较长或大对象直接分配到老年代。

对象主要分配在新生代的 Eden 区上,如果启动了本地线程分配缓冲,将按线程优先在 TLAB 上分配。少数情况下也可能直接分配在老年代中,分配的规则并不是百分之百固定的。

图片图片

大对象直接进入老年代

虚拟机提供了一个 -XX:PretenureSizeThreshold参数,令大于这个设置值的对象直接在老年代分配,这样做的目的是避免在 Eden 区和及两个 Survivor 区之间发生大量的内存复制。

长期存活的对象将进入老年代

如果对象在 Eden 出生并经过第一次 Minor GC 后仍然存活,并且能被 Survivor 容纳的话,将被移动到 Survivor 空间中,并且对象年龄设为 1。

对象在 Survivor 空间中每“熬过”一次 Minor GC,年龄就增加 1 岁,当它的年龄到达一定程度(最大为 15 岁),就将会被晋升到老年代。

对象晋升老年代的年龄阈值,可以通过参数 -XX:MaxTenuringThreshold 设置。

对象是否能够晋升到老年代,也不全由-XX:MaxTenuringThreshold 参数控制,如果 Survivor 空间中相同年龄的所有对象大小总和大于 Survivor 空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代。

空间分配担保

新生代在发生 Minor GC 之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象之和(或者历次晋升老年代对象的平均大小)。

如果这个条件不成立,那么虚拟机将直接进行 Full GC 动作;如果这个条件成立,那么虚拟机就会进行一次 Minor GC 操作,但是这次 Minor GC 是有风险的,因为比较的值是平均值,可能出现极端的情况 —— 大量对象在 Minor GC 后还存活,这时就只好在失败后重新发起一次 Full GC。

总结

本章深入解析了类加载机制对对象创建的支持,探讨了 JVM 的内存布局、访问方式及分配策略。

通过理解这些底层原理,开发者可以有效优化代码性能,并在内存问题排查中更加游刃有余。

来源:码哥跳动内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯