当前位置: 首页 > news >正文

图解Java数组的内存分布

我们知道,访问数组元素要通过数组索引,如:

arr[0]

如果直接访问数组,比如:

int[] arr1 = {1};
System.out.println(arr1);

会发生什么呢?

在这里插入图片描述
打印的是一串奇怪的字符串:[I@16b98e56

这个字符串是Java在打印对象时的特殊输出,包含如下三部分信息:

  • 对象类型,如上图的[符号表示是数组类型,I表示整形数组
  • @是分隔符,没有实际意义
  • 16b98e56是堆内存地址,通过这个变量找到数组后,才能对其进行访问和修改

也就是说,数组变量存储的不是数组,而是一个地址,这个地址指向了哪里呢

要回答这个问题,首先要了解下Java的内存分布原理。

一,Java的内存分布

1,Java虚拟机内存划分

Java将虚拟机内存分为如下几个区域:

  • 本地方法栈
  • 寄存器
  • 虚拟机栈
  • 方法区/元空间

这样需要注意的是:从JDK8开始,Java取消了方法区,将方法区拆分到堆区、堆外区、元空间,增加一个元空间区,同时增加了堆外内存的使用。

JDK8前的内存分布:

在这里插入图片描述

JDK8的内存分布:

在这里插入图片描述

2,基本类型和引用类型的内存分布差异

特别来看下堆和栈,这是最为核心、重要的两个内存区域:
在这里插入图片描述

根据堆栈知识来理解下基本类型和引用类型的不同,这两种类型的变量值都存储在栈内存,但变量值本身的意义有较大的区别:

  • ①基本类型变量的值直接存储在栈内存,不涉及堆内存
  • ②引用类型变量的值在栈内存存储的是对象的地址,指向对象在堆内存的地址

基本类型变量的内存分布(仅涉及栈内存):

在这里插入图片描述

引用类型变量的内存分布(涉及栈和堆):

在这里插入图片描述

二,一个数组的内存分布

对于如下代码:

int[] arr = new int[2];
sout(arr);
sout(arr[0]);
sout(arr[1]);

逐行解释如上代码:

第一行代码声明并初始化一个数组arr,Jvm会做两件事:

  • ①在栈内存分配一块内存,存储数组对象地址
  • ②在堆内心分配一块内存,存储数组元素

第二行代码打印数组地址

第三行代码访问数组第一个元素,Jvm会根据栈内存中数组变量存储的地址,找到堆内存中数组对象,访问并读取第一个元素,然后打印

第四行代码和第三行作用相同,不同的是读取的是第二个元素

在这里插入图片描述

三,两个或多个不同数组的内存分布

当我们创建两个或两个以上的不同数组时,如图所示,会在栈内存中创建两个变量,这两个变量存储两个不同的堆内存地址,指向堆中不同的两个内存区域,存放着不同的两个数组对象:

在这里插入图片描述

当我们打印这两个数组变量时:

System.out.println(arr);
System.out.println(arr2);

输出的地址不相,分别是:[I@0x110fa7f48[I@0xbec966a

三,两个变量指向同一个数组的内存分布

int[] arr1 = {11,22};
int[] arr2 = arr1;
sout(arr1);
sout(arr2);
sout(arr1[0]);
sout(arr2[0]);
arr2 = new int[]{11, 22}

逐行解释如上代码:

第一行代码声明并初始化一个数组arr1,Jvm做两件事:

  • ①在栈内存分配一块内存,存储数组对象地址
  • ②在堆内心分配一块内存,存储数组元素

第二行代码声明并初始化一个数组arr2,Jvm做两件事:

  • ①在栈内存分配一块内存
  • ②将arr1变量的值复制给arr2

此时,变量arr1和arr2指向了同一块堆内存

在这里插入图片描述

执行第三行第四行代码:

sout(arr1);
sout(arr2);

会打印两个一样的值:[I@0x0011

第五行代码访问数组arr1第一个元素,Jvm会根据栈内存中数组变量存储的地址,找到堆内存中数组对象,访问并读取第一个元素,然后打印。

第六行代码访问数组arr2第一个元素,因为arr1arr2指向同一个数组,所以打印结果和第五行一致。

接下来,请大家思考,执行第七行代码arr2 = new int[]{11, 22}后,会出现什么情况呢?

尽管这个数组的元素个数、值都和arr1一致,但是Jvm还是会在堆区创建一个新的数组,并将arr2的值覆盖为新的数组对象的地址:

在这里插入图片描述

如果再次执行第三行第四行代码:

sout(arr1);
sout(arr2);

打印的结果就不再相同了,分别是[I@0x33bab[I@0x453fe

五,结论

  • ①数组变量分配在栈内存,存储的是对象地址,这个地址指向堆内存
  • ②数组对象(数组大小、所有数组元素)本身存储在堆内存中
  • ③访问和修改数组要通过数组变量存储的地址,找到数组对象,然后访问和修改其中的元素值
  • ④如果两个数组变量指向同一个数组对象,则通过变量访问的是同一个对象。如果之后其中一个数组指向另一个对象,通过这两个变量访问的就不再是同一个对象了
http://www.lryc.cn/news/351376.html

相关文章:

  • 前端项目使用docker编译发版和gitlab-cicd发版方式
  • 18kw 机架式液冷负载的使用方法有哪些?
  • Linux liloconfig命令教程:创建和配置LILO引导加载器(附实例详解和注意事项)
  • 大厂程序员离职,开发一个盲盒小程序2万,一周开发完!
  • 自定义 Spring AOP 切面实战(鉴权、记录日志)
  • JAVA面试题大全(九)
  • React 组件三大核心之 ref
  • json 读写 python
  • Docker快速部署Seata的TC服务以及微服务引入Seata教程
  • 我的第一个JAVA程序IDEA版
  • 轻量SEO分析报告程序网站已开心去授权
  • 本机与华为云ping不通的问题
  • FastJSON2 > FastJSON 好在何处
  • 7个常见的SQL慢查询问题及其解决方法
  • 《Rust奇幻之旅:从Java和C++开启》第1章Hello world 1/5
  • 将富文本编辑器中的H标签处理成树形结构,支持无限层级
  • 探索移动云:我的ES与Kibana之旅
  • java 线程执行原理,java线程在jvm中执行流程
  • [Redis]基本全局命令
  • 【Linux】- HBase集群部署 [19]
  • js如何遍历FormData的值
  • 【C语言】明析部分C语言内存函数
  • 一阶数字高通滤波器
  • Linux多线程系列2: 模拟封装简易语言级线程库,线程互斥和锁,线程同步和条件变量,线程其他知识点
  • VUE3-form表单保存附件与基本信息
  • 无线网络安全技术基础
  • sheng的学习笔记-docker部署Greenplum
  • 【投稿资讯】区块链会议CCF A -- SP 2025 截止6.6、11.14 附录用率
  • C++哪些函数不能被声明为虚函数
  • vue中数据已经改变了,但是table里面内容没更新渲染!