内存管理概述
🎯 核心目标
- ▸ 自动化内存分配与释放
- ▸ 防止内存泄漏和野指针
- ▸ 优化内存使用效率
- ▸ 提供统一的内存抽象
🔧 关键组件
- ▸ 内存分配器 (Allocator)
- ▸ 垃圾回收器 (Garbage Collector)
- ▸ 引用计数 (Reference Counting)
- ▸ 内存池 (Memory Pool)
⚡ 性能考量
- ▸ 分配/释放速度
- ▸ 内存碎片化控制
- ▸ GC 暂停时间
- ▸ 缓存局部性
内存架构对比
PHP (Zend Engine)
应用层 (Application)
- PHP 代码 / 用户变量
- 函数调用栈
- 类实例化
Zend VM (Zend Engine)
- opcode 指令执行
- 符号表管理
- 作用域管理
Zend MM (内存管理器)
- emalloc/efree 封装
- 请求级内存池
- 引用计数管理
系统层
- glibc / jemalloc
- 操作系统 malloc
- 虚拟内存系统
Python (CPython)
应用层 (Application)
- Python 代码 / 用户对象
- 调用栈 (Frame Stack)
- 模块与命名空间
Python VM (字节码解释器)
- opcode 指令执行
- PyObject 管理
- 类型系统
PyMem (内存管理器)
- PyMem_Malloc/Free
- 对象分配器 (PyObject)
- 引用计数管理
系统层
- PyMalloc ( arenas )
- 虚拟内存页
- 底层内存分配
💎 PHP 特色:请求级内存池
PHP 每个请求结束时会释放所有内存,不会跨请求保留。这是 PHP 内存管理的最大特色,避免了内存泄漏但也意味着无法缓存大量数据。
引用计数机制
引用计数工作流程
创建对象
$a
赋值引用
赋值给另一个变量
$a
$b
unset/unset
释放引用
ref = 0
🐘 PHP 引用计数
- ▸
refcount字段存储在 zval 结构体中 - ▸ 每次赋值
$b = $a时 refcount++ - ▸ 每次 unset() 时 refcount--
- ▸ refcount=0 时立即调用 destructor
- ▸ 循环引用无法自动回收(需 GC 模块)
🐍 Python 引用计数
- ▸
ob_refcnt在 PyObject 头部 - ▸
b = a时 refcount++ - ▸ 变量销毁时 refcount--
- ▸ refcount=0 时立即释放
- ▸ 线程安全(有 GIL 保护)
写时复制 (Copy-on-Write)
阶段1: 初始赋值
refcount = 1
is_ref = 0
阶段2: $b = $a
refcount = 2
is_ref = 0
阶段3: $b = "New Value"
$a refcount = 1
$b 新建独立副本
⚠️ is_ref 与 COW 的关系
| 场景 | is_ref | refcount | 行为 |
|---|---|---|---|
$a = "hello"; $b = $a; |
0 | 2 | COW 共享内存 |
$a = "hello"; $b = &$a; |
1 | 2 | 引用,修改互相影响 |
$a = "hello"; $b = $a; $b[0] = "H"; |
COW 分离 | 各1 | $b 触发复制 |
垃圾回收机制
🐘 PHP 垃圾回收器
PHP 使用"引用计数 + 同步垃圾回收"策略。当可能存在循环引用时,触发 GC 算法。
PHP GC 流程
(refcount 减少但非零)
真正的垃圾
🐍 Python 垃圾回收器
Python 使用"引用计数 + 分代式 GC"策略。引用计数负责绝大多数对象的回收,分代 GC 处理循环引用。
分代回收策略
年轻代 (Gen 0)
频繁回收
大部分对象早亡
中年代 (Gen 1)
过渡区
垃圾比例降低
老年代 (Gen 2)
静态变量、类定义
优先保留
Python GC 触发条件
(标记-清除)
🔄 循环引用示例
PHP
Python
内存池机制 (Python)
PyMalloc 三层架构
第一层:Block(8字节对齐)
最小的内存单元,大小为 8, 16, 24, 32... 256 字节。请求大小向上取整到最近的 block 大小。
第二层:Pool(4KB 页)
大小相同的 block 组成 pool。一个 pool 通常是 4KB(一个内存页),管理同一大小的所有 block。
■ 已用 □ 空闲
第三层:Arena(256KB)
多个 pool 组成 arena。 arenas 从系统申请 256KB 的大块内存,内部包含多个 pools。
核心对比
| 特性 | PHP | Python |
|---|---|---|
| 主要策略 | 引用计数 + 同步 GC | 引用计数 + 分代 GC |
| 内存释放时机 | refcount=0 立即释放;GC 周期清理循环 | refcount=0 立即释放;分代 GC 清理循环 |
| 循环引用处理 | 需显式触发 GC | 自动分代回收 |
| 内存池 | 请求级内存池(请求结束全部释放) | PyMalloc(长期运行优化) |
| 写时复制 | 支持(Zend MM) | 不支持(直接赋值) |
| GC 暂停 | 仅在 gc_collect_cycles() 时 | 分代回收时可能 STW |
| 线程安全 | 取决于扩展(pthread) | 有 GIL 保护 |
| 适用场景 | Web 请求/响应模型 | 长期运行服务 |
| 内存泄漏风险 | 低(请求结束释放) | 较高(需主动清理) |
交互式演示
模拟 PHP 引用计数变化
变量表
内存对象
暂无对象
GC 状态
术语表
- zval (Zend Value)
- PHP 中表示值的结构体,包含类型、值、引用计数等信息
- PyObject
- Python 中所有对象的基类,包含引用计数和类型指针
- Copy-on-Write (COW)
- 延迟复制策略,多个引用共享同一份数据,修改时才复制
- Mark and Sweep
- 标记-清除算法,GC 通过标记可达对象来识别垃圾
- Generational GC
- 分代垃圾回收,基于"大多数对象早亡"的假设优化
- Arena
- Python 内存池中的 256KB 内存块,由系统分配
- Pool
- 同一大小的 blocks 组成的 4KB 内存池
- GIL (Global Interpreter Lock)
- Python 的全局解释器锁,保证引用计数的线程安全
- Memory Leak
- 内存泄漏,分配的内存在不再使用时未能释放
- Cycle Reference
- 循环引用,两个或多个对象互相引用形成环