内存到底是如何在编程中实际运作的(不使用行话,只求清晰)

发布: (2026年3月7日 GMT+8 00:10)
8 分钟阅读
原文: Dev.to

I’m happy to translate the article for you, but I need the actual text of the post. Could you please paste the content you’d like translated (excluding the source line you’ve already provided)? Once I have the article text, I’ll translate it into Simplified Chinese while preserving all formatting, markdown, and technical terms.

每个开发者每天都在使用内存。

但大多数教程跳过 why,直接进入语法。

本文将解决这个问题。阅读完后,你将了解代码运行时实际发生了什么——以及这为何对编写更好的软件至关重要。

仓库类比

想象你的计算机内存是一座拥有数百万编号货架的巨大仓库。

  • 每个货架存放一小块数据。
  • 每个货架都有唯一的地址(一个数字)。
  • 你的程序就是在这些货架上读取和写入的工人。

当你写下:

int age = 25;

你是在告诉计算机:“找一个空的货架,在上面写下数字 25,并把该货架的地址记作 age。”

就是这样。这就是变量。

栈 vs 堆 — 仓库的两个区域

这里是大多数人感到困惑的地方。仓库有 两个区域,规则截然不同。

🟧 栈 — 快速、组织化、临时的

把栈想象成自助餐厅里整齐堆放的托盘。

  • 每次调用函数时,都会在顶部放置一个新托盘。
  • 托盘保存该函数的所有局部变量。
  • 当函数结束时,托盘会立即被移除。
void greet() {
    String name = "Alice";  // lives on the Stack
    int age = 30;            // lives on the Stack
    System.out.println(name + " is " + age);
} // When greet() ends — name and age are GONE

栈会自行清理。你无需做任何操作——当函数返回时,内存会自动释放。

栈的特性

属性细节
速度分配/释放非常快
大小受限(通常为 1–8 MB)
存储局部变量、函数调用、基本类型
生命周期与创建它们的函数绑定

🟦 堆 — 灵活、强大、由你负责

堆是仓库的其余部分——庞大、无序且持久。

当你创建一个 对象 时,它会被放在堆上:

// The reference `person` lives on the Stack
// The actual Person object lives on the Heap
Person person = new Person("Alice", 30);

堆是对象所在的地方,因为:

  • 它们可以是 大型(图像、列表、复杂数据)。
  • 它们需要 超出 创建它们的函数的生命周期。
  • 代码的多个部分可能需要 共享 它们。

堆的特性

属性细节
速度分配较慢(必须找到空闲块)
大小远大于栈(仅受限于 RAM)
存储对象、数组、任何使用 new 创建的东西
生命周期直到没有任何引用为止

让所有人都困惑的引用技巧

下面这段代码几乎会让每个初学者都踩坑:

Person a = new Person("Alice");
Person b = a;  // 这到底发生了什么?

大多数人会认为 b 是 Alice 的一个副本。其实并不是。

b 是指向同一个堆上 Alice 对象的 地址副本(引用)。

Stack:                     Heap:
┌─────────────┐          ┌──────────────────┐
│ a → [0x4A2] │────────▶ │ Person("Alice") │
│ b → [0x4A2] │────────▶ │                  │
└─────────────┘          └──────────────────┘

所以当你这样写时:

b.name = "Bob";
System.out.println(a.name); // 输出 "Bob"!

ab 指向的是同一个对象。通过其中一个修改,它的变化另一个也能看到。

这就是为什么在面试中“按引用传递 vs 按值传递”如此重要的原因。

垃圾回收 — 自动清理器

在 Java、Python、JavaScript 等语言中,你不需要手动释放堆内存。垃圾回收器 (GC) 会为你完成这项工作。

GC 会定期扫描堆,寻找已经没有任何引用指向的对象:

Person person = new Person("Alice");
person = new Person("Bob");  // Alice 现在不可达 — GC 将会清理她

Alice 已经没有引用指向她了。她是垃圾(别介意,Alice)。GC 会回收她占用的内存。

带有 GC 的语言: Java、Python、JavaScript、C#、Go
不带 GC(手动内存管理)的语言: C、C++、Rust(所有权模型)

在 C 中,忘记释放内存会导致 内存泄漏 —— 程序会逐渐耗尽 RAM,直至崩溃。这也是为什么 C/C++ 开发者常被认为需要格外小心的原因。

为什么这很重要?

了解内存可以让你在实际工作中成为更好的开发者:

  1. 你将理解 NullPointerExceptions

    Person person = null; // reference points to nothing
    person.getName();      // CRASH — you’re asking for the address of nothing
  2. 你将编写更快的代码 – 访问栈内存显著快于堆内存。了解这一点有助于你在数据结构上做出更明智的决策。

  3. 你将调试内存泄漏 – 如果你的应用随时间变慢,说明有某些东西不该持有堆对象。了解内存工作原理能告诉你该检查哪里。

  4. 你将在面试中表现出色 – 栈 vs 堆、值传递 vs 引用、垃圾回收 … 这些话题在技术面试中经常出现。

快速参考摘要

StackHeap
Speed非常快较慢
Size小 (1‑8 MB)大 (GB,受内存限制)
Stores基本类型,引用对象,数组
Managed by由 CPU 自动管理垃圾回收或手动管理
Lifetime函数作用域直至未被引用

想深入了解吗?

祝编码愉快!

最后一点

内存管理是那类只需十分钟阅读就能为你节省十小时调试时间的话题之一。

下次你的代码因 NullPointerExceptionStackOverflowError 或神秘的性能下降而崩溃时——你将确切知道该去哪里查看。

觉得这篇有帮助吗?我在 TheCodeForge.io 上发布纯英文的编程教程——涵盖 Java、Python、数据结构与算法、JavaScript 等 1,057+ 篇教程。始终免费。

0 浏览
Back to Blog

相关文章

阅读更多 »

你的撤销按钮只是一堆煎饼

TL;DR:我使用 Stack 数据结构来实现撤销功能,因为它遵循后进先出(LIFO)原则。每一次状态变化都会被压入栈中……