Java对象在堆和栈上的存储(对象布局,待完善)

news2024/11/26 4:45:09

0、前言

这里提到的 Java 对象不仅仅包含引用类型(Object),还包含基本数据类型(boolean、int、long、float、double)。文中部分图片来源于 B站 黑马程序员。

1、在栈上的数据存储

1.1、局部变量

局部变量包含以下情况:

  • 方法中定义的变量
  • 方法的形参

注:在非 static 修饰的成员方法中,第一个形参是 this,代表当前类的实例对象

1.2、槽位(slot)

各种类型变量在堆空间和栈空间中的内存分配,常说的 int 占用 4B 是针对堆中变量,而在栈中是按照槽位(slot)进行分配的。

数据类型字节数(堆空间)槽位数(栈空间)
boolean1B1
char2B1
byte1B1
short2B1
int4B1
long8B2
float4B1
double8B2
Object见对象在堆上的数据存储中的相关讨论1

总结:

  • 1 slot = 机器字长(32 位机中 32 bit,64 位机中 64 bit)
  • longdouble 占用 2 个 slot
  • 其它类型占用 1 个 slot

1.3、堆数据和栈数据的赋值过程

一般情况而言,同类型变量在堆中的长度更短,在栈中的长度更长。总的转换思路为:

  • 堆 -> 栈:按符号位进行填充,负数在前面填充1,正数在前面填充0,能够保证在补码意义上值保持不变
  • 栈 -> 堆:截断 (boolean 类型比较特殊,只取最后 1bit,而不是 1B)

下面分别是 -5 和 5 的补码表示
在这里插入图片描述

复习点:给定一个负数,写出其补码

  1. 先写出其倒数(正数)的补码(即原码)
  2. 从右到左找到第一个1,左取反,右不变

1.4、测试案例

Java 代码

public class ObjectStackLayout {
    static class MyObject {
        String aString;

        Integer aInteger;
        int anInt;

        boolean aBoolean;

        double aDouble;
    }


    public static void main(String[] args) {
    
        boolean b = true;
        
        char ch = 'a';
        short sh = 10;
        int x = 1;
        
        float f = 1.0f;
        double d = 2.2;

        String s = "hello world";
        MyObject myObject = new MyObject();
    }


    static short num = -5;


    private void calculate(int x) {
        // 堆数据 -> 栈数据
        short y = num;

        // 栈数据 -> 堆数据
        num = y;
    }
}

main 方法的字节码

// boolean b = true
// 从istore_1指令可以看出,将b作为int类型处理(istore含义是int store)
0 iconst_1
1 istore_1
 
// char ch = 'a'
2 bipush 97
4 istore_2
 
// short sh = 10
5 bipush 10
7 istore_3

// int x = 1
8 iconst_1
9 istore 4

// float = 1.0f
// 使用fstore,说明float类型数据和int类型数据在栈上的存储不同
11 fconst_1
12 fstore 5


14 ldc2_w #2 <2.2>
17 dstore 6

19 ldc #4 <hello world>
21 astore 8

23 new #5 <org/example/layout/stack/ObjectStackLayout$MyObject>
26 dup
27 invokespecial #6 <org/example/layout/stack/ObjectStackLayout$MyObject.<init> : ()V>
30 astore 9

32 return

main 方法的局部变量表

槽总数 = 1 + 1 + 1 + 1 + 1 + 1 + 2 + 1 + 1 = 10

在这里插入图片描述
在这里插入图片描述

总结:

  • 形参也是局部变量,测试 calculate 方法可以看到为局部变量 this 分配槽位
  • 浮点数和整数之间使用不同的字节码指令

2、在堆上的数据存储

在这里插入图片描述

2.1、Java 对象的堆内存布局

在这里插入图片描述

标记字段(Mark Word)

标记字段取决于机器字长、是否开启指针压缩这两个因素,下图是 **64 位机开启指针压缩(默认情况)**的情况
在这里插入图片描述
上面共有 5 种状态,原本应该使用 3 bit 来表示锁的状态位,这会导致处于轻量级锁状态和重量级锁状态的对象少了 1 bit 的指针,这样锁数量的上限就变为原来的 1/2。因此,将正常状态和偏向锁的最后 2bit 相同,使用额外的 1bit 来区分正常状态和偏向锁状态。

可能需要注意的点:其中有 1bit 提供给 CMS 垃圾收集器进行使用,后面在 GC 相关文章中再考虑之间的关联

在 64 位机关闭指针压缩的情况下,只是简单地将 cms使用位 弃用。
在这里插入图片描述

在 32 位机的情况下,不存在 cms使用位,同时将高位 32 bit舍弃即可。
在这里插入图片描述

元数据指针(Klass pointer)

在这里插入图片描述

hsdb 工具进行验证:

2.2、布局规则

规则优先级从高到低依次为:

  1. 对象的总长度需要对齐 8B,不够则进行零填充
  2. 父类变量在子类变量之前
  3. 引用数据类型(Object)在基本数据类型(int、double)之后
  4. 若变量类型的长度为 n,则该变量的起始偏移必须是 k × \times × n,单位为 Byte
  5. 变量可以进行重排列,不一定要按照定义的先后顺序排列(满足规则3)
  6. 类型更长的变量排在前面,类型更短的变量排在后面(满足规则3)

2.3.1、内存对齐

在没有开启指针压缩的情况下,同样会进行内存对齐,原因是 64 位机上的 CPU 缓存行大小是 8B,保证对象对齐 8B,可以保证并发情况下,两个对象的修改不会因为缓存行而相互影响。

在这里插入图片描述

2.3.2、父类优先

2.3.3、基本类型优先

2.3.4、字段对齐

2.3.5、字段重排列

2.3、测试案例

<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.7</version>
</dependency>
class Parent {
    long l;
    int i;
}

class Child extends Parent {
    String name;
    boolean b;
    int i;
    long l;
}


public class ObjectHeapLayout {
    public static void main(String[] args) {
        // 测试父类中的实例变量一定在子类之前分配,并且引用类型一定在每个类的最后分配
        System.out.println(ClassLayout.parseInstance(new Child()).toPrintable());
        
        // 测试字符串的实际占用空间
        System.out.println(ClassLayout.parseInstance("123").toPrintable());
        
        // 测试数组(nums也是一个引用指针)
        int[] nums = new int[]{1, 2, 3};
        System.out.println(ClassLayout.parseInstance(nums).toPrintable());
        
        System.out.println(ClassLayout.parseInstance(new Object()).toPrintable());
        // System.out.println(ClassLayout.parseInstance(null).toPrintable());//抛出异常
    }
}

在这里插入图片描述

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

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

相关文章

过滤器Filter --学习笔记

什么是Filter&#xff1f; Filter表示过滤器&#xff0c;是 JavaWeb三大组件(Servlet、Filter、Listener)之一过滤器可以把对资源的请求拦截下来&#xff0c;从而实现一些特殊的功能过滤器一般完成一些通用的操作&#xff0c;比如&#xff1a;登录校验、统一编码处理、敏感字符…

NLP发展及其详解

一、RNN(循环神经网络) 在这里附上一个很好的笔记 零基础入门深度学习(5) - 循环神经网络 RNN(循环神经网络)的结构特点在于其循环单元的设计,这种设计允许网络在处理序列数据时保持对之前信息的记忆。下面详细解释RNN的结构: 循环单元:RNN的循环单元是网络的核心,它…

《QT实用小工具·四十五》可以在界面上游泳的小鱼

1、概述 源码放在文章末尾 该项目实现了灵动的小鱼&#xff0c;可以在界面上跟随鼠标点击自由的游泳&#xff0c;项目demo演示如下所示&#xff1a; 项目部分代码如下所示&#xff1a; #include "magicfish.h" #include <QtMath> #include <QPainter>…

怎么设置 idea terminal 窗口的编码格式

1 修改Terminal 窗口为 Git bash 窗口 打开 settings 设置界面&#xff0c;选择 Tools 中的 Terminal (File -> settings -> Tools -> Terminal) 修改 Shell path 为你的 Git bash 安装路径&#xff0c;我的在 C:\my_software\java\Git\bin\bash.exe 2 解决中文显示…

【UE5】数字人基础

这里主要记录一下自己在实现数字人得过程中涉及导XSens惯性动捕&#xff0c;视频动捕&#xff0c;LiveLinkFace表捕&#xff0c;GRoom物理头发等。 一、导入骨骼网格体 骨骼网格体即模型要在模型雕刻阶段就要雕刻好表捕所需的表情体(blendshape)&#xff0c;后面表捕的效果直…

干货满满,Apollo7周年大会的心得体会

在云计算、人工智能、大数据等技术的助力下&#xff0c;自动驾驶已成为现代科技的一个标志性领域。 Apollo是一个开放、完整、安全的自动驾驶平台&#xff0c;助力开发者快速搭建自动驾驶系统。 Apollo开放平台自诞生以来&#xff0c;就扮演着这一领域的引领者角色。而今&…

Zynq 7000 系列之启动模式—Quad-SPI启动

Quad-SPI启动是一种高效的闪存启动方式&#xff0c;它利用Quad-SPI接口的高速数据传输能力来加速启动过程。Quad-SPI&#xff08;四路串行外设接口&#xff09;是一种改进的SPI&#xff08;串行外设接口&#xff09;协议&#xff0c;通过使用四条数据线而不是传统的单条数据线&…

Typora配置PicGo图床,将图片文件上传到gitee厂库,获取图片链接显示在md文件中

Typora配置PicGo图床&#xff0c;将图片文件上传到gitee厂库&#xff0c;获取图片链接显示在md文件中 创建Gitee创库和配置私人令牌 名字、路径、描述自己随便添&#xff0c;但是必须开源&#xff0c;链接才能可以访问&#xff1a; 进入偏好设置 > 图像 > 选择PicGo-Cor…

基于java+springboot+vue实现的物流管理系统(文末源码+Lw)208

摘 要 社会发展日新月异&#xff0c;用计算机应用实现数据管理功能已经算是很完善的了&#xff0c;但是随着移动互联网的到来&#xff0c;处理信息不再受制于地理位置的限制&#xff0c;处理信息及时高效&#xff0c;备受人们的喜爱。本次开发一套物流管理系统有管理员和用户…

Windows使用bat远程操作Linux并执行命令

背景&#xff1a;让客户可以简单在Windows中能自己执行 Linux中的脚本&#xff0c;傻瓜式操作&#xff01; 方法&#xff1a;做一个简单的bat脚本&#xff01;能远程连接到Linux&#xff0c;并执行Linux命令&#xff01;客户双击就能使用&#xff01; 1、原先上网查询到使用P…

大厂常见算法50题-替换空格

专栏持续更新50道算法题&#xff0c;都是大厂高频算法题&#xff0c;建议关注, 一起巧‘背’算法! 文章目录 题目解法一 String类replace方法解法二 遍历替换总结 题目 解法一 String类replace方法 String类自带的replace&#xff0c;方法传入两个char类型的参数&#xff0c;分…

Pixelmator Pro for Mac:简洁而强大的图像编辑软件

Pixelmator Pro for Mac是一款专为Mac用户设计的图像编辑软件&#xff0c;它集简洁的操作界面与强大的功能于一身&#xff0c;为用户提供了卓越的图像编辑体验。 Pixelmator Pro for Mac v3.5.9中文激活版下载 该软件支持多种文件格式&#xff0c;包括常见的JPEG、PNG、TIFF等&…

Gromacs——教程学习(6)

谈谈怎么判断分子动力学模拟是否达到了平衡 在计算RMSD之前必须先通过最小二乘法将各帧结构相对于参考结构进行最大程度叠合&#xff0c;从而消除体系的整体运动而令RMSD只体现生物分子内部结构的变化&#xff0c;这称为align或者least squares fit。 需要注意的是&#xff0…

Rundeck(四)安全配置

自动化运维工具rundeck GitHub - rundeck 是java开发的开源自动化服务&#xff0c;具有 Web 控制台、命令行工具和 WebAPI。它使您可以轻松地跨一组节点运行自动化任务&#xff0c;适合运维自动化管理、自动发布管理、运维数据分析等 网站&#xff1a;https://www.rundeck.co…

【golang-ent】go-zero框架 整合 ent orm框架实现一对一 一对多 多种姿势查询方式

一、ent的 O2O 问题 官方文档如下: https://entgo.io/zh/docs/schema-edges#o2o-same-type 1、ent O2O问题 官方提供了三种 one2one的方式,可以看到他全部使用了 mysql的 foregionKey 的方式进行关联,虽然举例了单表和双表的不同使用方式,但是我们实际使用mysql中是不创建…

flutter笔记-webrtc使用1:依赖本地包socket.io-client

文章目录 1. 示例工程2. yaml 修改3. 使用4. socketio 关于自定义服务器自定义签名的问题封装成async和await方式 本文开始介绍webrtc的使用&#xff0c;阅读本文的前提是假设你已经使用过webrtc&#xff0c;了解webrtc的交互机制&#xff0c;不了解的可以看之前的文章&#xf…

【Python】全面掌握 Collections Deque:队列与栈的高效实现及动态内存管理指南

文章目录 第一章&#xff1a;deque 的定义和特性1. 什么是双端队列&#xff08;deque&#xff09;2. deque 与普通列表&#xff08;list&#xff09;的性能差异 第二章&#xff1a;构造函数1. 如何创建一个 deque2. 可选参数 maxlen 的作用和使用场景 第三章&#xff1a;添加和…

信息泄露后担心被恶意点了网贷怎么办?

在当今信息时代&#xff0c;个人信息泄露已成为一个普遍现象&#xff0c;而泄露的信息可能被不法分子用于进行恶意行为&#xff0c;如恶意申贷。一旦被恶意申贷&#xff0c;可能会导致信用受损、法律责任等一系列问题。那么&#xff0c;信息泄露后担心被恶意申贷了怎么办呢?本…

金三银四面试题(二十三):装饰器模式知多少?

什么是装饰器模式 装饰器模式&#xff08;Decorator Pattern&#xff09;是一种结构型设计模式&#xff0c;它允许动态地向对象添加新的行为&#xff0c;而无需修改原始对象的结构。通过将对象包装在一个或多个装饰器对象中&#xff0c;装饰器模式可以增强原始对象的功能。 装…

Swift - 枚举

文章目录 Swift - 枚举1. 枚举的基本用法2. 关联值&#xff08;Associated Values&#xff09;3. 关联值举例4. 原始值5. 隐式原始值&#xff08;Implicitly Assigned Raw Values&#xff09;6. 递归枚举&#xff08;Recursive Enumeration&#xff09;7. MemoryLayout Swift -…