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

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());//抛出异常
    }
}

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++:map和set的封装

关于红黑树的模拟实现&#xff0c;大家不清楚的先去看看博主的博客再来看这篇文章&#xff0c;因为set和map的封装底层都是利用用的红黑树。所以这里不会过多介绍红黑树的相关内容&#xff0c;而更多的是去为了契合STL中的红黑树去进行改造&#xff0c;让封装的set和map能够去复…

【Java】Java基础 使用集合实现斗地主分牌

&#x1f4dd;个人主页&#xff1a;哈__ 期待您的关注 今天使用集合TreeSet来实现一个斗地主的分牌流程。 TreeSet集合的一个特点就是 元素有序&#xff0c;这样就方便我们分的牌自动排序。 0.思路 1.创建玩家手牌集合 我们到时候分的牌都存储在这里&#xff0c;但你可能会…

华为静音模式指定联系人来电响铃

华为静音模式指定联系人来电响铃 本人7年水果转华为&#xff0c;手机常年静音但是还是想收到指定人来电的。水果这个地方做的是很方便的&#xff0c;直接添加紧急联系人&#xff0c;什么声音都没有&#xff0c;只有指定人的电话铃声 直接上结论&#xff0c;华为是不支持直接这样…

关于google search console工具提交sitemap.xml无法抓取的问题解决办法

其实这个问题很好解决。 第一种情况&#xff1a;利用工具为我们的网站自动生成静态的sitemap.xml文件。这种可以检查下是否完整&#xff0c;然后上传到根目录下去&#xff0c;再去google search console提交我们的网站地图。 第二种情况&#xff1a;同样利用工具自动生成动态s…

【kettle005】kettle访问Oracle数据库并处理数据至execl文件(已更新)

1.一直以来想写下基于kettle的系列文章&#xff0c;作为较火的数据ETL工具&#xff0c;也是日常项目开发中常用的一款工具&#xff0c;最近刚好挤时间梳理、总结下这块儿的知识体系。 2.熟悉、梳理、总结下Oracle数据库相关知识体系 3.欢迎批评指正&#xff0c;跪谢一键三连&am…

MQTT基础知识

mqtt_manul MQTT物联网协议的学习笔记 一、MQTT基础知识 主要优势 发布订阅模式&#xff0c;一对多消息发布基于 TCP/IP 网络连接消息Qos支持&#xff0c;可靠传输保证&#xff08;QoS机制保证可靠传输&#xff09;灵活的消息传输&#xff0c;不关心 Payload 数据格式&…

爬虫的实战应用之短信炸弹playwright现代网页测试工具

不讲废话&#xff0c;先上原理&#xff1a; 短信炸弹&#xff0c;也就是说持续对一个手机进行发送短信&#xff0c;实现的方式就是&#xff0c;利用某些网站的登录 &#xff0c;注册的时候&#xff0c;发送短信验证码来实现。 如下图&#xff0c;其中有一个id为phone的输入框&a…

如何收集EMC Data Domain 的日志

遇到复杂问题&#xff0c;您第一时间需要做的就是收集日志&#xff0c;下面对EMC DataDomain 存储系统&#xff08;有人也叫做VTL虚拟带库&#xff09;如何收集日志做一个详细说明&#xff1a; EMC DD的日志分为两种&#xff0c;一种是Autosupport&#xff0c;一种是support b…

STM32-TIM定时器与PWM输出

学习目标&#xff1a; 1. 熟练掌握 TIM 的参数配置。 2. 掌握通道的参数配置。 3. 深刻理解 PWM 与功率的关系。 4. 理解 PWM 的原理示意。 一 什么是 PWM 输出 PWM &#xff08; pulse width modulation &#xff09;一种脉冲宽度调节技术。 PWM 的效果是什么样子&#xf…

【kettle006】kettle访问华为openGauss高斯数据库并处理数据至execl文件

1.一直以来想写下基于kettle的系列文章&#xff0c;作为较火的数据ETL工具&#xff0c;也是日常项目开发中常用的一款工具&#xff0c;最近刚好挤时间梳理、总结下这块儿的知识体系。 2.熟悉、梳理、总结下华为openGauss高斯数据库相关知识体系 3.欢迎批评指正&#xff0c;跪谢…

10.通用定时器

驱动电机 RGB LED亮度&#xff08;呼吸灯&#xff09; 舵机&#xff08;遥控车、机械臂&#xff09; 通用定时器作用 1.延时 2.定时器更新中断 3.输出比较&#xff08;PWM波、驱动IO输出波形&#xff08;脉冲&#xff09;&#xff09; 4.输入捕获&…

C语言——小知识和小细节17

一、未能给指针成功赋值 #include <stdio.h> #include <stdlib.h> #include <string.h>void GetMemory(char* p) {p (char*)malloc(20 * sizeof(char)); }void Test() {char* str NULL;GetMemory(str);strcpy(str, "Hello World!");printf(&quo…

分布式ID之雪花算法

1. Seata对雪花算法生成ID的改良 滑动验证页面 &#xff08;含代码&讲解&#xff09; Seata基于改良版雪花算法的分布式UUID生成器分析&#xff1a;时间戳和节点ID位置进行了调换。官网&#xff1a;Seata基于改良版雪花算法的分布式UUID生成器分析 | Apache Seata关于新版…

【平台开发】MTK6833 实现lk下CQE接口移植 - cmdq irq

1.cmdq_irq 检测中断bit 2.目前遇到问题 任务执行后&#xff0c;没有触发对应中断&#xff0c;伴有错误发生&#xff0c;但任务完成标志位能检测到 寄存器CQIS,CQDQS等均为0&#xff0c;CQTCN为任务完成寄存器看到置1&#xff0c;CQTERRI检测到8000错误 错误详情如下&#xf…

MATLAB : interp1()用法介绍

目录 一、基本语法&#xff1a; 二、实例&#xff1a; 1.样条拟合减振器阻尼曲线 ​2.PP拟合时间温度曲线 interp1 是 MATLAB 中的一个函数&#xff0c;用于在一维数据上执行插值操作。这个函数可以帮助你估计或计算已知数据点之间未知点的值。以下是 interp1 函数的基本用…

计算机网络复习和重点

一、计算机网络概述 网络发展历史、网络的定义、分类和组成、 网络协议分层模型介绍&#xff08;OSI和TCP/IP模型的比较&#xff09; 重点&#xff1a;网络协议的概念、分层 分层的目的&#xff1a;降低复杂性分层的好处和坏处&#xff1a;好处是设计简答&#xff0c;坏处是…

免费分享一套微信小程序扫码点餐(订餐)系统(uni-app+SpringBoot后端+Vue管理端技术实现) ,帅呆了~~

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的微信小程序扫码点餐(订餐)系统(uni-appSpringBoot后端Vue管理端技术实现) &#xff0c;分享下哈。 项目视频演示 【免费】微信小程序扫码点餐(订餐)系统(uni-appSpringBoot后端Vue管理端技术实现) Java毕…

基于数据湖的企业中台解决方案

什么是数据湖? 数据湖是一个以原始格式(通常是对象块或文件)存储数据的系统或存储库。数据湖通常是所有企业数据的单一存储。用于报告、可视化、高级分析和机器学习等任务。数据湖可以包括来自关系数据库的结构化数据(行和列)、半结构化数据(CSV、日志、XML、JSON)、非结构化…

封装umi-request时通过 AbortController 配置取消请求

一、关键部分 一、在封装的request.ts中 声明一个 abortControllers 对象用于存储要取消的请求&#xff08;我用了-s表示复数&#xff0c;多个abortcontroller对象&#xff0c;与下面&#x1f447;的单个abortController区分&#xff09;封装取消请求的函数cancelRequest, 传入…

Rust Web开发实战:打造高效稳定的服务端应用

Rust Web开发实战&#xff1a;打造高效稳定的服务端应用 本书将带领您从零开始构建Web应用程序&#xff0c;无论是API、微服务还是单体应用&#xff0c;都将一一涵盖。您将学到如何优雅地对外开放API&#xff0c;如何连接数据库以安全存储数据&#xff0c;以及如何对应用程序进…