Java 中堆内存和栈内存上的数据分布和特点

news/2025/2/11 11:18:01/文章来源:https://www.cnblogs.com/emanjusaka/p/18709398

博客:https://www.emanjusaka.com
博客园:https://www.cnblogs.com/emanjusaka
公众号:emanjusaka的编程栈

by emanjusaka from https://www.emanjusaka.com/archives/java-heap-stack-distribution-feature
本文为原创文章,可能会更新知识点以及修正文中的一些错误,全文转载请保留原文地址,避免产生因未即时修正导致的误导。

经常有人把 Java 内存区域笼统地划分为堆内存(Heap)和栈内存(Stack),这种划分方式直接继承自传统的 C、C++程序的内存布局结构,在 Java 语言就显得有些粗糙了,实际的内存区域划分是要更复杂一下。如下所示:

30-内存管理

方法区、堆是由所有线程共享的数据区。虚拟机栈、本地方法栈和程序计数器是线程隔离的数据区。

我们最关注的、与对象内存分配关系最密切的区域是“堆”和“栈”两块。其中“栈”通常就是指这里的虚拟机栈,更多情况下只是指虚拟机栈中局部变量表部分。下面我们详细分析一下堆内存和栈内存的数据分布。

问题:哪些数据放在栈上,哪些数据放在堆上?

如果你擅长 Java 这种内存自动管理的语言,这个问题很好回答。

栈上的数据:

  • 基本数据类型(booleanbytecharshortintfloatlongdouble

  • 对象引用(reference 类型,它并不等同于对象本身,可能是一个指向对象起始地址的应用指针,也可能是指向一个代表对象的句柄或者其他与此对象相关的位置)

  • returnAddress 类型(指向了一条字节码指令的地址)

堆上数据:

  • 普通对象:各种类的实例。

  • 数组:数组是一种特殊类型的对象,可以存储多个相同类型的元素。

  • 基本类型包装器对象:Java 提供了一些基本类型的包装器类:如 Integer、Double、Character 等。

栈和堆内存上的数据特点

我们先来分析下程序中的栈和堆,然后总结出它们的特点。

Stack

栈的数据结构特点是先进后出。由于这个特点,非常适合记录程序的函数调用,也称为函数调用栈。函数调用栈从下到上增长,每当函数执行时,就会在栈顶部分分配一块连续的内存,称为帧。这个帧存储了当前函数的通用寄存器和当前函数的局部变量的上下文信息。下面给出一个简单的 Java 函数调用,我们分析一下这个过程:

 public class StackExample {  
     public static void main(String[] args) {  
         int result = add(3, 5);  
         System.out.println("结果是: " + result);  
     }  
 ​  
     public static int add(int a, int b) {  
         return a + b;  
     }  
 } 在这个例子中,`main`函数调用了`add`函数。当`main`函数开始执行时,会在栈内存中为`main`函数分配一块空间,包括局部变量`result`和参数`args`。然后,`main`函数调用`add`函数,此时会在栈内存中为`add`函数分配另一块空间,包括局部变量`a`、`b`和返回地址。当`add`函数执行完毕后,其占用的栈空间会被释放,控制权返回给`main`函数。最后,`main`函数执行完毕,整个程序结束。通常情况下,它需要连续的内存空间,这意味着程序在调用下一个函数之前必须知道下一个函数需要多少内存空间。但是程序是怎样知道的呢?答案是编译器为我们完成了这一切。当编译代码时,函数是一个最小的编译单位。每当编译器遇到一个函数时,它就知道当前函数使用寄存器和局部变量所需的空间。因此,无法在编译时确定大小或可以更改大小的数据是不能安全地放置在栈上的。### Heap有些数据不能安全地放在栈上,所以最好放在堆上,比如下面的ArrayList:```java
 import java.util.ArrayList;
 public class VariableLengthArrayExample {
     public static void main(String[] args) {
         // 创建一个空的 ArrayList
         ArrayList<Integer> arrayList = new ArrayList<>();
         // 向 ArrayList 中添加元素
         arrayList.add(1);
         arrayList.add(2);
         arrayList.add(3);
         // 输出 ArrayList 的大小
         System.out.println("Size of the ArrayList: " + arrayList.size());
         // 访问 ArrayList 中的元素  
         for (int i = 0; i < arrayList.size(); i++) {  
             System.out.println("Element at index " + i + ": " + arrayList.get(i));  
         }
         // 删除 ArrayList 中的一个元素  
         arrayList.remove(1);
         // 再次输出 ArrayList 的大小和内容  
         System.out.println("Size of the ArrayList after removal: " + arrayList.size());  
         for (int i = 0; i < arrayList.size(); i++) {  
             System.out.println("Element at index " + i + ": " + arrayList.get(i));  
         }
     }
 }

当创建一个ArrayList 时,程序需要动态的分配内存。如果数组的实际使用量超过了这个容量,程序会分配一个更大的内存块,将现有元素复制到其中,添加新元素,然后释放旧内存。此过程允许数组根据需要动态调整大小。请求系统调用并找到新的内存然后一一复制的过程是非常低效的。所以这里最好的做法是提前预留需要的空间。

另外,需要跨栈引用的内存也需要放在堆上,这很好理解,因为一旦一个栈帧被回收,其内部的局部变量也会被回收,所以在不同的调用栈中共享数据只能使用堆。

总结

  • 栈上存储的数据是静态的,大小固定,生命周期固定,线程隔离不能跨栈引用。

  • 堆上存储的数据是动态的、不固定大小、不固定生命周期、线程共享可以跨栈引用。

参考资料

  • 《深入理解 Java 虚拟机(第 3 版)》——周志明

30-yiyan

谦学于心,谷纳万物,静思致远,共筑收获之旅!
原文地址: https://www.emanjusaka.com/archives/java-heap-stack-distribution-feature
微信公众号:emanjusaka的编程栈

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AspNetCore 实战:三种流式响应机制详解

在现代Web应用中,实时数据传输和高效的数据流处理变得越来越重要。AspNetCore 提供了多种流式响应机制,以满足不同场景下的需求。 在使用ChatGpt,deepseek的适合有没有想过ai的逐字显示回答是怎么实现的,下面将介绍三种主要的流式响应来实现此功能。 Server-Sent Events (S…

java中反射-字节码和类加载器

多态的一个表现 子类类型赋值给父类 Father f1 = New Son() 调用子类方法报错。 调用父类方法OK。这个就是多态 一个对象能用什么方法,并不是取决于 它有什么方法。 而是取决于引用变量的类型(也就是取决于它声明的类型,Father类型) 它能够用的方法,一定是Father中的方法。 …

本地私有化部署DeepSeek,打造自己的大模型知识库

1简介 DeepSeek R1是基于MIT协议开源的大模型,意味着个人和企业可以免费使用,包括商业使用。 众所周知,我们可以在chat.deepseek.com上可以直接免费使用DeepSeek R1的聊天服务。但如果你自己开发的应用需要使用到其api的话,仍然是需要收费的,另外,如果你有机密信息并不想…

五. 数据库

数据库 一. 三级模式 一个数据库可以有多个外模式,只能有一个内模式。 视图对应外模式、基本表对应模式、存储文件对应内模式二. 两级映像 (1) 模式/内模式映像。存在于概念级和内部级之间,实现了概念模式和内模式之间的相互转换。 (2) 外模式/模式映像。存在于外部级和概念级…

Oracle 缩容量方法 --转发 https://www.modb.pro/db/1873006442018521088?utm_source=index_ai

1.系统表空间清理 SYSAUX表空间被称为系统辅助表空间,主要的目的是为SYSTEM表空间减负。1.1 表空间的使用率 比如大表大部分都是AUD$和WRH$开头的AWR基表,AUD$使用SYSTEM表空间,AWR的基表使用SYSAUX表空间。 SELECT df.tablespace_name, COUNT (*) datafile_count,ROUND (SUM…

轻松上手!小白必看:在你的个人电脑上安装DeepSeek R1 大模型

deepseek本地化部署,小白教程,让你的个人电脑也能安装运行大模型!一、deepseek简介 DeepSeek是一个强大的工具,它就像一个聪明的助手,能帮我们处理和分析大量信息。它适用于各种场合,比如理解文字、识别图片,还能根据你的喜好推荐东西。 这个工具很灵活,就像乐高积木,…

macOS Sequoia 15.3.1 (24D70) 正式版 ISO、IPSW、PKG 下载

macOS Sequoia 15.3.1 (24D70) 正式版 ISO、IPSW、PKG 下载macOS Sequoia 15.3.1 (24D70) 正式版 ISO、IPSW、PKG 下载 iPhone 镜像、Safari 浏览器重大更新和 Apple Intelligence 等众多全新功能令 Mac 使用体验再升级 请访问原文链接:https://sysin.org/blog/macOS-Sequoia/…

macOS Sequoia 15.3.1 (24D70) Boot ISO 原版可引导镜像下载

macOS Sequoia 15.3.1 (24D70) Boot ISO 原版可引导镜像下载macOS Sequoia 15.3.1 (24D70) Boot ISO 原版可引导镜像下载 iPhone 镜像、Safari 浏览器重大更新和 Apple Intelligence 等众多全新功能令 Mac 使用体验再升级 请访问原文链接:https://sysin.org/blog/macOS-Sequoi…

如何利用看板工具,让项目进度一目了然?

使用看板评估项目进度需要经历准备、实施、评估与分析以及持续改进与优化等多个阶段。通过合理利用看板工具,团队可以更加高效地管理项目进度,提高项目管理的透明度和协作效率。看板工具最早起源于20世纪50年代的日本丰田生产系统,如今已被广泛应用于软件开发、产品管理、市…

ABB IRB360并联机器人|abb蜘蛛手维修

ABB IRB360并联机器人(也被称为蜘蛛手机器人)发挥着极为重要的作用。然而,如同所有的机械设备一样,它也会面临需要维修的情况。对ABB IRB360并联机器人进行有效的维修不仅能延长其使用寿命,还能确保工业生产的高效进行。一、ABB IRB360并联机器人的结构与常见故障1. 结构特…

为何焦点已从 AI 代理转向AI辅助人类工作模式

前言:随着AI技术的发展,AI行业从AI代理转向了‘AI辅助人类工作模式’。虽然AI代理在演示中很有潜力,但在实际使用时,它的准确性和可靠性远低于人类,限制了它的应用。相反,‘AI辅助人类工作模式’通过将复杂任务拆分成小步骤,帮助人类更高效地完成工作,推动了数据整理和…

CentOS7 离线安装docker环境

1、系统要求 首先需要确定 CentOS7 的内核版本号,因为 docker 安装要求 Linux 内核版本在 3.10 及以上。查看内核版本号: uname -r查看系统名称: cat /etc/redhat-release2、下载Docker Docker官网:Docker: Accelerated Container Application Development Docker引擎安装说…