堆和栈的概念和区别

embedded/2024/11/13 5:17:16/

文章目录

  • 堆和栈的概念和区别
    • 栈 (Stack)
    • 堆 (Heap)
    • 详细描述
    • 补充说明
      • 逃逸分析 (Escape Analysis)
      • 栈上分配 (Stack Allocation)
      • 堆碎片化 (Heap Fragmentation)

堆和栈的概念和区别

堆和栈的概念和区别【改编自博客】

在说堆和栈之前,我们先说一下JVM(虚拟机)内存的划分:

Java程序在运行时都要开辟空间,任何软件在运行时都要在内存中开辟空间,Java虚拟机运行时也是要开辟空间的。JVM运行时在内存中开辟一片内存区域,启动时在自己的内存区域中进行更细致的划分,因为虚拟机中每一片内存处理的方式都不同,所以要单独进行管理。

JVM内存的划分有五片:

  1. 寄存器;
  2. 本地方法区;
  3. 方法区;
  4. 栈内存;
  5. 堆内存。

栈 (Stack)

  • 存储局部变量:栈主要用于存储局部变量,包括方法参数、局部变量等,for循环内部定义的也是局部变量。这些变量通常是在方法调用开始时创建,在方法结束时销毁。
  • 方法调用:每当一个方法被调用时,都会为该方法创建一个新的栈帧来保存局部变量表、操作数栈、动态链接信息以及返回地址等。
  • 快速分配与释放:由于栈内存的管理是线程性的且按照后进先出的原则工作,因此分配和回收的速度非常快。
  • 生命周期短暂:栈中的数据生命周期较短,当它们的作用域结束时(例如,方法执行完毕),这些数据就会被自动释放。

堆 (Heap)

  • 存储对象实例:堆主要用于存储由 new 关键字创建的对象实例以及数组。每个对象都有一个指向其类元数据的指针。
  • 动态分配:堆内存中的空间是在运行时动态分配的,并且对象可以长期存在,直到被垃圾回收器回收。
  • 垃圾回收:Java 通过自动垃圾回收机制来管理堆内存,当对象不再被引用时,它们会被标记为垃圾并最终被回收。
  • 非线程私有:与栈不同,堆内存是所有线程共享的,这意味着所有线程都可以访问堆中的对象。

详细描述

下面我们通过一个图例详细讲一下堆和栈:

比如主函数里的语句 int [] arr=new int [3]; 在内存中是怎么被定义的:

在Java中,程序的执行从main方法开始。main方法是程序的入口点,它被定义为静态方法,位于类的内部。当JVM启动并准备执行这个方法时,它会为main方法创建一个新的栈帧,并将其压入调用栈中。栈帧是调用栈的一部分,用于存储局部变量、参数以及返回地址等信息。

当main方法被调用时,一个新的栈帧被创建并压入栈中。这个栈帧包含main方法的局部变量表、操作数栈、动态链接和方法出口信息。局部变量表用于存储方法的参数和局部变量,操作数栈用于执行方法中的操作,动态链接用于确定方法调用的目标,方法出口信息用于处理方法的返回和异常。

在main方法中定义的变量arr是一个引用,它指向堆中创建的数组对象。数组对象通过new关键字在堆中创建,并分配内存空间。堆中的数组对象会被初始化,数组元素根据其类型被赋予默认值。堆中的对象通过内存地址来访问,这个地址是连续的二进制值。栈中的变量(引用)保存了这个内存地址,通过这个引用可以访问堆中的对象。

总结来说,main方法被压入栈中是因为它是程序的入口点,需要一个栈帧来存储其局部变量、参数和返回地址等信息。在main方法中定义的变量arr是一个引用,它指向堆中创建的数组对象。数组对象通过new关键字在堆中创建,并分配内存空间。堆中的数组对象会被初始化,数组元素根据其类型被赋予默认值。堆中的对象通过内存地址来访问,这个地址是连续的二进制值。栈中的变量(引用)保存了这个内存地址,通过这个引用可以访问堆中的对象。

在这里插入图片描述

那么堆和栈是怎么联系起来的呢?

在Java中,arr是一个引用变量,它指向堆中创建的数组对象。当arr需要操纵数组时,它通过内存地址来访问堆中的数组对象,而不是直接将数组对象赋给arr。这种数据类型被称为引用数据类型,因为它引用了堆内存中的实体。

在C或C++中,指针的概念与Java中的引用类似。指针是一个变量,它存储了内存地址,通过这个地址可以访问内存中的数据。Java中的引用变量与C或C++中的指针有一些相似之处,但也有重要的区别。例如,Java中的引用变量是类型安全的,而C或C++中的指针需要显式地进行类型转换。此外,Java中的引用变量是由垃圾回收器管理的,而C或C++中的指针需要手动管理内存。

总之,Java中的引用变量与C或C++中的指针有一些相似之处,但也有重要的区别。Java中的引用变量是类型安全的,由垃圾回收器管理,而C或C++中的指针需要显式地进行类型转换和手动管理内存。

在这里插入图片描述

补充说明

逃逸分析 (Escape Analysis)

  • 定义:逃逸分析是一种编译器优化技术,它分析对象的作用域,判断一个对象是否“逃逸”出了当前方法的作用域,也就是说,判断该对象是否可能被其他方法访问。
  • 目的:如果一个对象仅在一个方法内部使用,并且不会被其他任何地方引用,那么它可以被视为不会“逃逸”出去。
  • 优化:对于不逃逸的对象,JVM 可以选择不在堆上为它们分配内存,而是直接在栈上分配。这样做的好处是可以避免垃圾回收的开销,并提高程序的性能。

栈上分配 (Stack Allocation)

  • 概念:栈上分配是指将原本应该在堆上分配的对象直接放在栈上的一种优化方式。
  • 适用场景:
    • 当一个对象的生命周期很短,且只在局部范围内使用。
    • 对象大小较小,适合放在栈上。
  • 优点:
    • 减少了垃圾回收的压力。
    • 加快了对象的创建和销毁速度。
    • 减少了内存碎片问题。

堆碎片化 (Heap Fragmentation)

  • 定义:随着对象的创建和销毁,堆内存中可能会出现许多不连续的小块空闲内存,这使得大块内存分配变得困难。
  • 影响:碎片化会导致内存分配效率降低,甚至导致内存不足的情况发生。
  • 解决方法:
    • 压缩式垃圾回收:某些垃圾回收器会在清理内存的同时重新整理内存中的对象,减少碎片。
    • 分代收集:JVM 通常会将堆分为新生代和老年代,新生代中使用复制算法来减少碎片。

http://www.ppmy.cn/embedded/102217.html

相关文章

RIP路由信息协议

基础知识 RIP:是一种内部网关协议(IGP),属于动态路由协议,主要用于自治系统(AS)内部的路由选择,RIP使用距离矢量算法来计算到达网络目的地的最佳路径,并通过定期更新路由信息来维护网络拓扑。 使用UDP协议在520端口上发送路由信息 度量值:…

实现通用人工智能 (AGI) 面临的挑战

根据徐博文的分享内容,实现通用人工智能 (AGI) 面临着以下几个主要挑战: 1. 智能定义的模糊性: 目前对智能的定义存在多种观点,例如解决困难问题、认知功能、适应环境等,缺乏统一的共识。不同定义会导致不同的研究方…

docker轻松集成延迟队列插件

在大家看这篇博客之前,先请大家去看博主的前一篇博客: Docker化RabbitMQ:轻松实现消息队列的部署与配置-CSDN博客 先安装rabbitmq,再去安装延迟队列。 1.任何目录都可以 cd /usr/local/docker/rabbitmq/ 2.联网下载 2.1rabbit…

计算机网络-HTTP协议详解

HTTP(Hypertext Transfer Protocol)即超文本传输协议,是互联网上应用最为广泛的一种网络传输协议。它基于客户端-服务器架构,用于在Web浏览器和Web服务器之间传输超文本(如HTML页面)和各种类型的数据&#…

Android广播机制简介

Android广播机制简介 记得在我上学的时候,每个班级的教室里都会装有一个喇叭,这些喇叭都是接入到学校的广播室的,一旦有什么重要的通知,就会播放一条广播来告知全校的师生。类似的工作机制其实在计算机领域也有很广泛的应用&…

复现ssrf漏洞

目录 一、pikachu靶场 1、靶场环境: 使用docker拉取: docker run -d -p 8765:80 8023/pikachu-expect:latest 2、使用dict 3、使用file读取文件 二、redis未授权访问 1、源码 2、使用bp探测端口 3、继续使用bp探测172.18.0.2的端口 4、使用go…

【Material-UI】Radio Group中的 Label Placement 属性详解

文章目录 一、Radio Group 组件概述1. 组件介绍2. Label Placement 属性的作用 二、Label Placement 属性的基本用法三、Label Placement 属性详解1. 标签位置的选择2. 如何在实际项目中选择标签位置 四、Label Placement 属性的实际应用场景1. 表单布局中的应用2. 符合用户习惯…

网络安全实训六(靶机实例DC-3)

1 信息收集 1.1 获取靶机IP 1.2 扫描靶机网站的目录 1.3 扫描端口和服务器信息 1.4 进入网站 1.5 在msf中给搜索joomla扫描器 1.6 设置参数查看joomla版本信息 1.7 按照版本号搜索漏洞 1.8 查看漏洞使用 2 渗透 2.1 查看是否存在SQL注入 2.2 获取到数据库信息 2.3 爆破列表 2…