Java数组的内存图

一、Java内存分配的重要性

Java内存分配的重要性在于它直接影响着程序的性能、可伸缩性和稳定性。以下是一些关键方面:

  1. 性能: 内存分配对程序的性能有着直接影响。不合理的内存分配可能导致频繁的垃圾收集(GC)和内存碎片化,从而降低应用程序的性能。适当的内存分配可以减少垃圾收集的频率,提高程序的响应速度和吞吐量。
  2. 垃圾收集(GC):Java程序通过垃圾收集器来管理内存。频繁的垃圾收集会导致应用程序的停顿时间增加,从而影响用户体验和系统的可用性。优化内存分配可以减少垃圾收集的次数和停顿时间。
  3. 内存泄露: 不正确的内存管理可能导致内存泄漏,即程序在不再需要使用的内存时无法释放它们。内存泄漏会导致内存消耗过多,最终导致系统崩溃或变慢。正确的内存分配和释放可以避免内存泄漏的发生。
  4. 可伸缩性:优化的内存分配可以提高应用程序的可伸缩性。通过减少内存占用和提高内存利用率,可以更好地支持大规模并发和高负载情况,从而提高系统的可伸缩性。
  5. 稳定性:不合理的内存分配可能导致应用程序崩溃或异常,从而影响系统的稳定性。通过合理的内存管理,可以减少由于内存相关的错误而导致的系统崩溃或异常,提高系统的稳定性和可靠性。

因此,Java内存分配的重要性在于它直接影响着程序的性能、可伸缩性和稳定性,是开发高性能、高可用性Java应用程序的关键因素之一。

二、Java内存分配

Java中的jvm会像应用程序一样占据一块内存空间

为了更好的利用这块空间,虚拟机把它分成了五个部分,每个部分都有自己的作用

分别是:栈、堆、方法区、本地方法栈、寄存器

1、栈:

  • 栈是线程私有的,用于存储基本数据类型变量和对象的引用。
  • 每个线程都有自己的栈,用于存储方法调用、局部变量以及操作数栈等。
  • 栈中的数据是线程安全的,因为每个线程都有独立的栈空间。
  • 方法运行时使用的内存,比如main方法运行,进入方法栈中执行

2、堆(Heap):

  • 堆是用于存储对象实例的内存区域,是Java中所有线程共享的部分。
  • 在堆中分配的内存由垃圾收集器管理,用于存储所有通过 new 关键字创建的对象。
  • Java堆是Java虚拟机管理的最大的一块内存,也是垃圾收集器管理的主要区域之一。

3、方法区(Method Area):

  • 存储可以运行的class文件
  • 方法区也称为永久代,是存储类的结构信息、常量、静态变量、即时编译器编译后的代码等数据的区域。
  • 在JDK8之前,方法区是使用永久代实现的;在JDK8及之后的版本中,永久代被元空间(Metaspace)取代。
  • 方法区与堆一样,是所有线程共享的区域。

4、本地方法栈(Native Method Stack):

  • 本地方法栈与栈类似,但是它是为Java虚拟机调用本地方法服务的。
  • 本地方法栈中存储的是本地方法(即用C/C++等编写的)的信息。

5、寄存器(Registers):

  • 寄存器并不是Java虚拟机规范中定义的内存区域,而是位于CPU内部的高速存储器。
  • 寄存器用于存储Java虚拟机中的局部变量、操作数栈等数据,它们在执行Java字节码时被使用。

其中方法区,在jdk7以前是和堆空间连在一起的,它们在物理内存中也是一块连续的空间

这样的设计方法有点不好,以至于从jdk8开始,取消方法区,新增元空间。把原来方法区的多种功能进行拆分,有的功能放到了堆中,有的功能放到了元空间中。

在正常开发中,栈和堆是一直使用的,所以介绍堆和栈

栈执行的顺序是先入后出,即开始就会将程序的主入口main方法加入栈中,直到程序运行完毕才会出栈。

堆内存中,主要存储的是new出对象的地址值

三、内存

1、示例

首先看一下简单代码的内存:

我们只需要关心栈和堆就行了,但是在这个代码中,只需要看栈就行了(没有涉及到new关键字)

当程序刚开始运行的时候,main方法就会进入栈底

从第一行开始,逐行进行运行代码

首先执行 int a = 10;此时栈中就会开辟一块小空间定义一个变量,它的名字就为a,还会给该变量a进行一个类型限定为int,然后将10放入小空间中。

int b = 10同理

int c = a+b;它首先也会开辟一块空间,然后将a和b的值拿出来进行相加,得到一个20,再把20赋值给变量c

System.out.println(c);先找到c变量的值,然后再打印变量c的值

内存图如下:

2、数组内存

了解完简单内存解析,接下来就看数组的内存

从数组的定义、获取、赋值方面进行解析

定义:

由于出现了new关键字,此时就要考虑栈和堆两个空间

首先程序开始运行,main方法加入栈中

首先执行第一行 int [] arr = new int [2];定义一个数组

这个代码是由左右两个部分组成的,int [] arr 进入栈中,右边 int[]进入堆中

因为长度为2,所有由0,1两个索引,索引所对应的值都是0,因为数组是int类型的,初始值为0

堆空间中的数组是有地址值的,它会将这个地址值赋值给栈空间中的左边的数组变量int[]arr,左边也需要通过这个地址值来找到右边的数组。

变量里面存的都是真实的数字,而数组变量存的是地址值,因为数组中有大量数据,没有办法将它一次存在arr这个变量中。所以Java在设计的时候,他就将这些数据放在另外一个空间中(堆)通过地址值将他们联系起来

左边为栈,右边为堆:

获取:

所以当我们运行第二行代码,打印数组的时候,所打印的是数组的地址值。

所以我们需要通过数组名加索引的方式进行取值(arr[0]),在内存中它是通过arr找到右边堆中的空间,然后再通过索引进行获取值

赋值:

其实赋值和获取类似,arr[0]=11;就是将11赋值给arr的0索引。首先通过地址值找到堆内存中的小空间,然后再把11赋值给0索引,原来的值就会给覆盖了,此时数组中存储的就是新的元素了。

两个数组的内存:

int [] arr2 = {33,44,55};

虽然这个数组创建中没有new关键字,但是我们要知道,这个是简化的书写方式,完整的书写方式也会有new关键字

此时堆内存中,又会开辟一块新的空间,与之前创建的是相互独立的,两者之间没有任何影响

完整内存图:

总结:

1、只要是new出来的一定实在堆里面开辟了一个 小空间

2、如果new了多次,那么再堆里会有多个小空间,每个空间都有各自的数据

四、两个数组指向同一个空间的内存图

先看一段代码:

两个数组指向同一个空间,当arr2修改值时,arr1也会被修改

由于arr1中保存的是,堆空间中的地址值,当我们将arr1赋值给arr2的时候,相当于把地址值赋值给了arr2,此时arr1和arr2的地址值是一样的,所以他们通过地址值在堆空间中找到的是同一块空间,修改的值也是一样的值,所以才会出现上述情况。

  • 29
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱摸鱼的呆瓜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值