一、堆(Heap)与栈(Stack)#

Heap(堆)和 Stack(栈)都在内存中,它们是同一块物理内存(电脑的 RAM)被划分出来的两个不同用途的区域。一个是快速自动回收小区(栈),另一个是存放大对象、需要 GC 打扫的大区(堆)。

场景:你写了一个方法,叫 买东西():

Java

void 买东西() {   
 int 钱 = 1000;           *// ← 这就是“栈”上的东西*    
 String 购物车 = new String("苹果");  *// ← 这就是“堆”上的东西*
 }

当你调用 买东西() 这个方法时:

  1. 栈(上下铺)发生了什么?

    • 系统立刻在栈上给你分配一个小空间。

    • 把 钱 = 1000 这个临时变量放进去。

    • 方法开始执行……

    • 方法一结束(买完东西了),栈上的 钱 立刻自动消失,空间马上回收。

    • 特点:快、自动、干净,像住酒店,退房就走。

  2. 堆(大小区)发生了什么?

    • new String(“苹果”) 这句话会在上申请一个大房间。

    • “苹果”这个对象就住在堆里。

    • 即使 买东西() 方法结束了,只要还有其他地方拿着“购物车”这个钥匙(引用),这个“苹果”对象就继续住在堆里

    • 只有当没有任何人再用它时,垃圾回收器(GC)才会过来把它清理掉。

特点栈(Stack)堆(Heap)
存放什么基本类型变量、对象引用、方法参数真正 new 出来的对象
内存大小很小(几 MB 到几十 MB)很大(几百 MB 到几十 GB)
分配和释放速度非常快较慢
是否有碎片没有(严格按顺序进出)有(会产生内存碎片,靠 GC 整理)
线程安全每个线程都有自己的栈,互不干扰所有线程共享一个堆,需要小心并发
出错时提示栈溢出(StackOverflowError)堆内存不足(OutOfMemoryError)
谁来清理方法结束,系统自动清理垃圾回收器(GC)自动清理

总结:

:短、快、小、自动 → 放临时数据,像住酒店,住完就走;是用来放“临时小东西”的,速度飞快,自动管理

:长、慢、大、需要 GC → 放真正重要的对象,像买房子,住很久,脏了由保洁阿姨(GC)来打扫;堆是用来放“真正大对象”的,空间大,但需要垃圾回收器帮忙清理

栈溢出:你写了一个无限递归的方法(方法不停地调用自己),栈空间很快就满了 → 程序直接崩溃(StackOverflow)。

堆内存溢出:你不停地 new 对象,但从来不让它们被回收 → 堆慢慢被撑爆 → OutOfMemoryError。

二、GC垃圾回收–用来处理堆数据#

自动回收程序中不再使用的堆内存空间,防止内存泄漏,让程序能持续稳定运行。安全地 **释放这些对象占用的内存,**把释放出来的内存重新交给程序使用(分配新对象)。

它解决了什么问题?

  • 在 C/C++ 中,程序员必须手动 malloc() 分配内存,手动 free() / delete 释放。

    • 忘记释放 → 内存泄漏(程序越跑内存占用越高,最终崩溃)

    • 提前释放 → 悬挂指针(使用已释放的内存,导致崩溃或安全漏洞)

  • Java / Go 等语言引入垃圾回收器后,程序员完全不用手动管理内存,把这个繁琐且容易出错的工作交给垃圾回收器自动完成。

根本作用:自动释放不再使用的堆内存。

直接好处:程序员不用操心内存释放,避免内存泄漏和 dangling pointer(悬挂指针)。

实际效果:让程序长时间运行也不会因为内存耗尽而崩溃(当然,仍然可能因为 GC 频繁或Stop-The-World(STW)导致性能问题)。