前言
所有的程序都必须和计算机内存打交道,从计算机申请内存,在不需要的时候释放内存,这是各个编程语言的设计难点。
目前有三种流派的内存管理:
- 垃圾回收机制(GC):在运行时不断的寻找需要释放的内存。JavaScript、Java 等等各大主流语言都用的 GC
- 手动管理内存的分配和释放:所有堆对象的创建和销毁都要由程序员负责。C++
- 通过所有权进行管理内存:在编译时根据一系列规则进行检查。Rust
为什么要学内存管理?
帮助开发同学更好的理解各种变量创建的过程,应用的性能情况有哪些优化空间,帮助我们开发高性能的应用,尤其是 Node 应用。
内存管理的知识是通用的,你在开发 JS、JAVA、C++、GO 等等各种语言都绕不开它。
栈(Stack)和堆(Heap)
数据结构中的栈和堆
一般我们看到栈和堆就会以为是两个数据结构:
栈是一个后进先出的数据结构,比如 JS 中的 函数调用栈、浏览器的网页历史记录

堆 是一种特别的完全二叉树的数据结构,还分为最大堆、最小堆,一般适用于优先级队列,比如 React 的 Scheduler

内存中的栈和堆
我们主要是讲 C/C++ 的内存分配。( V8 引擎是 C++ 实现的,Java 中的 JVM 是 汇编、C、C++ 实现的,比较有代表性)
我们这里所说的堆栈是两种内存管理的形式,所以也会叫栈内存(栈区)和堆内存(堆区)。
在 C/C++ 中内存分为 栈区、堆区、全局/静态存储区、常量存储区、代码区。
**静态内存分配:**编译时分配。包括:全局、静态全局、静态局部三种变量。
**动态内存分配:**运行时分配。包括:栈、堆。

栈内存
-
是由编译器管理的
-
是线程私有的。
-
栈中的数据占有大小是已知的。
-
每个方法在执行时都会创建一个栈帧用于存储局部变量、操作数栈、方法出口等等信息。
哪些是存到栈内存的?:
字符(char)、数值(int)、布尔值、引用指针等等,可以看出来,这些值一旦声明了,它的大小是确定的,编译期就能知道的。
堆内存
- 是由程序员管理的
- 所有线程共享的
- 可动态分配和释放的内存
- 堆中的数据都是大小不可知,也可能会发生变化。
由此可以看出,我们一般说的 内存溢出、GC 等都是指的是堆内存。
哪些是存到堆内存的?:
容器:动态数组(C++ 种的 Vector,JS 中的 Array)、哈希表、JS 中的对象。
从这些可以看出来,这些变量声明后,大小是不确定的,编译器是不可知的。运行时会动态的变大变小。

垃圾回收机制
垃圾回收机制的实现有两种:
-
引用计数收集器:最早的也是最简单的垃圾回收实现方法。会将存到堆空间的对象
当引用一个对象,计数器就加一,不引用计数器就减一,为 0 时释放空间
- 优点:可以尽快的回收不适用的对象
- 缺点:频繁更新计数会降低效率、循环引用问题等等