JVM对象创建全流程解析

一、JVM对象创建流程

在这里插入图片描述

Ⅰ、类加载检查——JVM创建对象时先检查类是否加载

在虚拟机遇到new指令时,比如new关键字、对象克隆、对象序列化时,如下字节码

0: new           #2                  // class com/example/demo/Calculate

检查指令的参数(#2)是否能在常量池中定位到一个类的符号引用

常量池:
Constant pool:#1 = Methodref          #7.#27         // java/lang/Object."<init>":()V#2 = Class              #28            // my/Calculate#3 = Methodref          #2.#27         // my/Calculate."<init>":()V#4 = Fieldref           #29.#30        // java/lang/System.out:Ljava/io/PrintStream;#5 = Methodref          #2.#31         // my/Calculate.compute:()I#6 = Methodref          #32.#33        // java/io/PrintStream.println:(I)V#7 = Class              #34            // java/lang/Object#8 = Utf8               <init>#9 = Utf8               ()V#10 = Utf8               Code#11 = Utf8               LineNumberTable#12 = Utf8               LocalVariableTable#13 = Utf8               this#14 = Utf8               Lmy/Calculate;#15 = Utf8               compute#16 = Utf8               ()I#17 = Utf8               a#18 = Utf8               I#19 = Utf8               b#20 = Utf8               main#21 = Utf8               ([Ljava/lang/String;)V#22 = Utf8               args#23 = Utf8               [Ljava/lang/String;#24 = Utf8               calculate#25 = Utf8               SourceFile#26 = Utf8               Calculate.java#27 = NameAndType        #8:#9          // "<init>":()V#28 = Utf8               my/Calculate#29 = Class              #35            // java/lang/System#30 = NameAndType        #36:#37        // out:Ljava/io/PrintStream;#31 = NameAndType        #15:#16        // compute:()I#32 = Class              #38            // java/io/PrintStream#33 = NameAndType        #39:#40        // println:(I)V#34 = Utf8               java/lang/Object#35 = Utf8               java/lang/System#36 = Utf8               out#37 = Utf8               Ljava/io/PrintStream;#38 = Utf8               java/io/PrintStream#39 = Utf8               println#40 = Utf8               (I)V

检查符号引用代表的类是否已经被加载、校验、准备、解析和初始化,如果没有加载,通过类加载机制加载类。

Ⅱ、分配内存——创建对象的一大工作就是分配内存

由于类一旦被加载,就可知该类对象所占内存空间(因为对象头大小、属性-每个类型占用多少字节是固定的)

为对象分配内存,就是从堆或者栈(一般是堆)中为分配一块确定大小的空间

划分内存的方式——通过指针碰撞或者空闲列表的方式分配内存空间:

  • 指针碰撞:默认使用的方式,通过一个指针标识当前已经使用到位置,指针一侧是已分配的空间、另一次是未使用的空闲内存,通过指针移动对象所需空间大小来分配内存。要求java堆内存绝对规整,已用空间分配在一侧。
  • 空闲列表:通过维护一张空闲列表维护空闲空间的初始位置和块大小,通过在空闲列表寻找可用的内存块(对象所需空间>空闲块时,该空闲块不可用),分配并更新空闲列表。

并发分配问题——在分配内存的时必然存在多个线程为对象在堆中分配空间(堆是线程共享的区域),就是存在并发分配内存的问题,解决方法:

  • CAS锁+失败重试:CAS-Compare And Swap

  • TLAB:本地线程分配缓存-Thread Local Allocate Buffer,先为每个线程在java堆中分配一块空间,当为该线程的对象分配内存时,先从预分配内存中进行分配(打破了线程竞争同一块堆空间的问题)

    -XX:+UseTLAB(默认开启)、-XX:TLABSize设定预分配内存空间大小

Ⅲ、初始化——为分配给对象的内存空间赋0值,不包括对象头

如果是TLAB(本地线程分配缓存)的分配方式,则初始化提前到为每个线程在java堆中分配一块空间时进行。

这一过程使java的实例变量和类变量可以在不赋初始值就可使用,只是访问出的是该类型的0值。

  • 对于基本数据类型(如 intdoublechar 等),如果没有显式初始化,它们的默认值如下:

    • int 类型的变量默认值为 0

    • double 类型的变量默认值为 0.0

    • char 类型的变量默认值为 '\u0000'(即空字符)。

    • public class Person {int age; // 没有初始化,默认为0String name; // 没有初始化,默认为null
      }
      Person person = new Person();
      System.out.println(person.age); // 输出 0
      System.out.println(person.name); // 输出 null
      
  • 对于对象引用类型(如类、接口、数组等),如果没有显式初始化,它们的默认值是 null

  • 局部变量:在Java中,局部变量(在方法内部声明的变量)如果不初始化就直接使用,编译器会报错,因为局部变量在使用前必须显式初始化。

public void test() {int x; // 编译错误:局部变量x可能尚未初始化System.out.println(x);
}

Ⅳ、设置对象头

对象

  • 对象头
    • 标记字段(Mark Word):占用内存视操作系统,32位的占4字节(32bit),64位的占8字节(64bit),包括锁标志位、对象的hashcode、分代年龄、偏向线程ID、偏向锁时间戳(Epoch)、锁指针。锁标志位内容不同则保存的对象信息不同。
    • 类型指针(Klass Pointer):占用内存视是否开启指针压缩,开启指针压缩占用4字节,不开启占用8字节,默认开启。是指向元空间中类的元数据信息的指针,JVM通过这个指针判断该对象是哪个类的实例。
    • 数组长度(如果对象是数组类型):如果对象是数据类型,存储数组长度,占用4字节。
  • 实例数据
  • 对齐填充

以下表格是32位的操作系统下默认开启指针压缩的对象头:

标记字段的结构 类型指针
25bit4bit1bit2bit4字节
23bit2bit是否偏向锁锁标志位
对象的哈希码分代年龄001(无锁)
线程ID:持有偏向锁的线程ID,标识哪个线程偏向该对象Epoch:偏向锁的时间戳,用于批量撤销偏向锁分代年龄101(无锁)
指向栈中锁记录的指针00(轻量级锁)
指向重量级锁指针(操作系统级互斥锁)10(重量级锁)
11(GC标记,表示对象待回收,由GC算法确定)

Ⅴ、执行方法

执行方法,按照程序员的意愿进行初始化,为属性赋值(赋程序员给的值)和执行构造方法。

二、查看对象大小和指针压缩

1、查看对象的内存布局

引入依赖

        <dependency><groupId>org.openjdk.jol</groupId><artifactId>jol-core</artifactId><version>0.17</version></dependency>

示例代码

package com.example.demo;import org.openjdk.jol.info.ClassLayout;/*** 计算对象大小*/
public class JOLSample {public static void main(String[] args) {ClassLayout layout2 = ClassLayout.parseInstance(new A());System.out.println(layout2.toPrintable());}// ‐XX:+UseCompressedOops 默认开启的压缩所有指针// ‐XX:+UseCompressedClassPointers 默认开启的压缩对象头里的类型指针Klass Pointer// Oops : Ordinary Object Pointerspublic static class A {//8B mark word//4B Klass Pointer 如果关闭压缩‐XX:‐UseCompressedClassPointers或‐XX:‐UseCompressedOops,则占用8Bint id; //4BString name; //4B 如果关闭压缩‐XX:‐UseCompressedOops,则占用8Bbyte b; //1BObject o; //4B 如果关闭压缩‐XX:‐UseCompressedOops,则占用8B}
}

在64位操作系统上的执行结果(默认开启指针压缩):

在这里插入图片描述

关闭指针压缩后,引用类型占用的空间变成8字节:

在这里插入图片描述

根据提供的 JOL(Java Object Layout)输出,以下是 com.example.demo.JOLSample$A 对象的内存布局分析:

  1. 对象头(Object Header)

    • Mark Word(标记字):
      • 偏移量:0,大小:8 字节
      • 值:0x0000000000000005
      • 含义:表示对象处于 可偏向状态biasable),分代年龄为 0age: 0),存储锁、GC 状态等信息。
    • Klass Word(类指针):
      • 偏移量:8,大小:4 字节
      • 值:0xf800cf18
      • 含义:指向类元数据的指针(JVM 开启指针压缩后为 4 字节)。
  2. 实例字段(Instance Fields)

    • int id
      • 偏移量:12,大小:4 字节
      • 值:0(默认初始值)。
    • byte b
      • 偏移量:16,大小:1 字节
      • 值:0(默认初始值)。
    • 对齐填充(Padding Gap)
      • 偏移量:17,大小:3 字节
      • 原因:下一个字段 String name 需对齐到 4 字节边界(20 是 4 的倍数),因此在 byte b 后填充 3 字节。
    • String name
      • 偏移量:20,大小:4 字节
      • 值:null(引用类型,指针压缩后占 4 字节)。
    • Object o
      • 偏移量:24,大小:4 字节
      • 值:null(引用类型)。
  3. 对象对齐填充(Object Alignment Gap)

    • 偏移量:28,大小:4 字节
    • 原因:对象总大小需对齐至 8 字节(64 位 JVM 的默认对齐)。当前已用 28 字节(0~27),需填充至 32 字节(28 + 4 = 32)。

关键指标

  • 实例总大小(Instance Size)32 字节。
  • 空间损失(Space Losses)
    • 内部(Internal)3 字节(字段间填充)。
    • 外部(External)4 字节(对象末尾填充)。
    • 总计损失7 字节。

内存布局图示

偏移量大小(字节)内容说明
08Mark Word锁、GC 状态等
84Klass Word类元数据指针
124int id整型字段
161byte b字节字段
173对齐填充补齐至 4 字节边界
204String name字符串引用(null
244Object o对象引用(null
284对象对齐填充补齐至 8 字节边界

总结

  • 对象头占 12 字节8 + 4),字段数据占 13 字节4 + 1 + 4 + 4),但实际占用 20 字节(含内部填充)。
  • JVM 通过填充确保字段对齐和对象对齐,提高内存访问效率。
  • 优化建议:若需减少空间,可调整字段顺序(如将 byte b 放在末尾),但 JVM 会自动重排,通常无需手动干预。
2、指针压缩的JVM配置参数

‐XX:+UseCompressedOops :开启的压缩所有指针,默认开启

‐XX:+UseCompressedClassPointers :开启的压缩对象头里的类型指针Klass Pointer,默认开启

3、为什么要有指针压缩

1、在64位的平台中节约空间和带宽:在主内存和缓存之间复制较大指针会占用更多带宽;

2、32位地址最大支持4G内存,通过对对象指针的压缩编码、解码以支持更大的内存配置(不超过32G);

3、堆内存小于4G时不需要开启指针压缩,JVM会自动去除高32位地址,使用低虚拟地址空间;

4、堆内存大于32G时,压缩指针失效,强制使用64位对java对象寻址。(所以堆内存不建议大于32G)

ressedOops :开启的压缩所有指针,默认开启

‐XX:+UseCompressedClassPointers :开启的压缩对象头里的类型指针Klass Pointer,默认开启

3、为什么要有指针压缩

1、在64位的平台中节约空间和带宽:在主内存和缓存之间复制较大指针会占用更多带宽;

2、32位地址最大支持4G内存,通过对对象指针的压缩编码、解码以支持更大的内存配置(不超过32G);

3、堆内存小于4G时不需要开启指针压缩,JVM会自动去除高32位地址,使用低虚拟地址空间;

4、堆内存大于32G时,压缩指针失效,强制使用64位对java对象寻址。(所以堆内存不建议大于32G)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/85815.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/85815.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深度学习从入门到精通:PyTorch实战与核心原理详解

掌握深度学习核心概念&#xff0c;玩转PyTorch框架&#xff0c;从理论到实战一站式学习指南 &#x1f680; 一、深度学习全景图 &#x1f31f; 人工智能金字塔 &#x1f50d; 深度学习核心优势 ​​优势​​​​劣势​​​​适用场景​​自动特征提取依赖大数据图像识别&…

计算机网络期末 物理层

目录 数据通信基础(理解) 传输介质(熟悉) 基带传输(熟悉) 数字编码(熟悉) 频带传输与调制解调(理解) 多路复用技术(了解) 物理层设备与极限速率(掌握) 数据通信基础(理解) 一堆概念 通信的类型 同步技术 传输介质(熟悉) 有线介质 同轴电缆 双绞线 光纤 无线介质 无线电…

力扣-139.单词拆分

题目描述 给你一个字符串 s 和一个字符串列表 wordDict 作为字典。如果可以利用字典中出现的一个或多个单词拼接出 s 则返回 true。 注意&#xff1a;不要求字典中出现的单词全部都使用&#xff0c;并且字典中的单词可以重复使用。 class Solution {public boolean wordBrea…

LeetCode-1679. K 和数对的最大数目

给你一个整数数组 nums 和一个整数 k 。 每一步操作中&#xff0c;你需要从数组中选出和为 k 的两个整数&#xff0c;并将它们移出数组。 返回你可以对数组执行的最大操作数。 地址&#xff1a;https://leetcode.cn/problems/max-number-of-k-sum-pairs/description/?envTyp…

相机camera开发之差异对比核查四:测试机和对比机的Camera动态参数差异对比及关键字

【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、背景 二、:Camera动态参数差异 2.1:动态参数差异核查项 2.2 :动态参数差异核查关键字 2.3 :前置普通拍照动态参数 2.4 :后置普通拍照动态参数 2.5 :后置人像模式…

ModbusTCP转Profibus网关在配料系统中的配置实践

在现代饲料企业的生产过程中&#xff0c;自动化技术的应用日益广泛。其中&#xff0c;ModbusTCP和Profibus是两种常见的工业通信协议&#xff0c;它们在数据采集和设备控制方面发挥着重要作用。然而&#xff0c;由于这两种协议在技术上的差异&#xff0c;直接互通往往存在困难。…

双馈风机分段控制策略的一次调频模型深度解析

双馈风机分段控制策略的一次调频模型深度解析 摘要 随着风电渗透率的急剧攀升,电力系统惯性降低与一次调频能力弱化问题日益凸显。双馈感应发电机(DFIG)凭借其优越的性能已成为主流机型,但其常规控制策略使其自然不具备响应系统频率变化的能力。本文深入探讨基于分段控制策…

JMeter 高阶玩法:分布式压测的技术核心技术要点

在2025年的数字化浪潮中&#xff0c;网站和应用的性能直接决定用户体验和业务成败&#xff01;想象一下&#xff0c;双十一促销期间&#xff0c;你的电商平台因无法承受高并发而崩溃&#xff0c;或者金融系统在高峰期响应迟缓——这不仅是技术问题&#xff0c;更是商业灾难&…

在 Windows 和 Linux 下使用 C/C++ 连接 MySQL 的详细指南

前言 MySQL 是一种流行的关系型数据库管理系统&#xff0c;广泛应用于各种应用程序中。C/C 作为高性能编程语言&#xff0c;常被用于需要与数据库交互的开发中。下面详细讲解如何在 Windows 和 Linux 平台下使用 C/C 连接 MySQL 数据库&#xff0c;帮助你快速上手。 准备工作 …

【人工智能基础】初识神经网络

初识神经网络 本章通过战胜人类围棋世界冠军的AlphaGo案例,介绍神经网络的基本概念,并阐明其与人工智能、机器学习的关系。 1. AlphaGo与围棋:神经网络的实力展示 传统围棋程序:基于固定规则 早期的计算机程序依赖人类专家预先设定的策略(“如果A情况发生,则执行B步骤”…

ffmpeg webm 透明通道视频转成rgba图片

import subprocess def webm_to_bgrapng(webm_video_path,bgra_dir):command [ffmpeg,-vcodec, libvpx-vp9, # 指定输入视频编码为 VP9-i, webm_video_path, # 输入视频路径-pix_fmt, "rgba", # 输出 RGBA 格式&#xff08;保留 Alpha 通道&#xff09;bgra_dir …

SQLite 数据库操作完整指南

SQLite 数据库操作完整指南 全面的 SQLite 数据库操作手册&#xff0c;涵盖从基础操作到高级优化的所有内容 目录 ** SQLite 简介与特点 创建和连接数据库 创建表 数据类型和约束 插入数据 查询数据 更新数据 删除数据 多表查询 视图 索引优化 触发器 事务处理 全文搜索 JSO…

Python Luigi 【工作流管理库】简介

想全面了解DeepSeek的看过来 【包邮】DeepSeek全攻略 人人需要的AI通识课 零基础掌握DeepSeek的实用操作手册指南【限量作者亲笔签名版售完即止】 玩转DeepSeek这本就够了 【自营包邮】DeepSeek实战指南 deepseek从入门到精通实用操作指南现代科技科普读物AI普及知识读物人工智…

微服务中分布式事务:Saga模式、TCC模式与消息队列

Saga模式 Saga模式是一种基于补偿的事务管理机制&#xff0c;它将一个长事务分解为多个本地事务&#xff0c;每个本地事务都有一个对应的补偿事务。当某个本地事务执行失败时&#xff0c;Saga模式会依次调用前面已成功执行的本地事务的补偿事务&#xff0c;以实现事务的回滚。…

唯美复古风景人像摄影Lr调色教程,手机滤镜PS+Lightroom预设下载!

调色教程 “唯美复古风景人像摄影 Lr 调色”&#xff0c;是将人物置于如画的风景之中进行拍摄&#xff0c;再运用 Lightroom&#xff08;Lr&#xff09;软件&#xff0c;通过专业的调色操作&#xff0c;为照片赋予复古的艺术气息&#xff0c;让画面兼具唯美的视觉享受与怀旧的情…

华为云Flexus+DeepSeek征文|体验华为云ModelArts快速搭建Dify-LLM应用开发平台并创建b站视频总结大模型

华为云FlexusDeepSeek征文&#xff5c;体验华为云ModelArts快速搭建Dify-LLM应用开发平台并创建b站视频总结大模型 什么是华为云ModelArts 华为云ModelArts ModelArts是华为云提供的全流程AI开发平台&#xff0c;覆盖从数据准备到模型部署的全生命周期管理&#xff0c;帮助企…

线程池异步处理

List<CompletableFuture<Void>> futures new ArrayList<>();// 提交10个异步任务 for (int i 0; i < 10; i) {CompletableFuture<Void> future CompletableFuture.runAsync(() -> {insertData(batchData); // 每个任务插入一批数据}, pool).t…

STM32HAL库 -- 8.串口UART通信并开启printf功能

目录 1.简介 2.串口和UART 2.1串口的简介 2.2UART的简介 2.3UART通信协议 2.3.1波特率 2.3.2空闲位 2.3.3起始位 2.3.4数据位 2.3.5校验位 2.3.6停止位 3.STM32的UART 4.HAL库中常用的操作UART的函数 4.1UART初始化函数 -- HAL_UART_Init 4.2硬件初始化回调函数…

【PyTorch项目实战】CycleGAN:无需成对训练样本,支持跨领域图像风格迁移

文章目录 一、风格迁移模型&#x1f3a8;1、发展时间线2、分类与优缺点3、选择建议4、HuggingFace Demo&#xff08;instruct-pix2pix&#xff09; —— 在线测试 二、论文简读&#xff08;1&#xff09;FastStyleTransfer&#xff08;快速风格迁移&#xff0c;Johnson et al.,…

C#Halcon从零开发_Day14_AOI缺陷检测策略1_Bolb分析+特征分析_饼干破损检测

一、引言 *缺陷检测策略1&#xff1a;Blob分析特征分析 *Blob分析&#xff1a;阈值分割、开闭运算 (1) 图像预处理 目的&#xff1a;增强目标与背景的对比度&#xff0c;抑制噪声。 常用算子&#xff1a; threshold()&#xff1a;通过阈值分割将图像转换为二值图像。 mean_…