memory_get_usage()返回值偏低并非函数故障,而是默认仅统计PHP用户态堆内存(emalloc分配),不包含OPcache、扩展malloc内存、GC未触发的悬空内存及共享库/线程栈等RSS组成部分;设true参数可纳入未归还系统页内存,但仍非全量快照。

memory_get_usage() 返回值偏低不是函数坏了,而是它默认不统计 PHP 引擎内部未释放但已标记为“可回收”的内存块 —— 垃圾回收(GC)没触发时,这部分内存仍算“已分配”,但 memory_get_usage(false) 不计入。
为什么 memory_get_usage() 看起来比实际占用少
PHP 的内存统计基于 emalloc() 分配器,只跟踪自己分配的堆内存。但以下几类内存它压根不看:
-
OPcache缓存的 opcode 占用(进程级 RSS 里有,memory_get_usage()里没有) - 扩展模块(如
curl、gd)调用系统 malloc 分配的内存 - 对象引用被 unset 后、GC 尚未运行前的“悬空”内存(仍被 Zend 引用计数器持有)
- PHP 8.0+ 的周期性 GC 默认延迟触发,
gc_collect_cycles()不手动调就一直攒着
real_usage = true 能解决多少问题
设为 true 后,memory_get_usage(true) 会向系统申请实际分配的内存页大小(含对齐和未使用页),更接近 ps aux 看到的 RSS,但仍不包含:
- 共享库(
libc、libssl)映射的只读段 - 线程栈空间(每个 Worker 线程约 8MB,默认不计入)
- PHP 进程启动时预分配但未使用的内存池(尤其在
opcache.memory_consumption较大时)
所以 memory_get_usage(true) 是个“上限逼近”,不是“全量快照”。若你看到它比 ps RSS 小 10MB 以上,大概率是共享库或线程栈占的。
立即学习“PHP免费学习笔记(深入)”;
GC 不触发时的典型误判现象
常见于循环创建对象又 unset 的场景,比如:
$arr = [];
for ($i = 0; $i < 10000; $i++) {
$arr[] = new stdClass();
}
// 此时 memory_get_usage() 可能只增不减
unset($arr);
// 内存没立刻回落?别急 —— GC 没跑
gc_collect_cycles(); // 手动触发后才可能看到下降
关键点:
- PHP 7.3+ 默认
zend.enable_gc=1,但 GC 触发条件是“根缓冲区满”(默认 10000 个节点)或显式调用 -
gc_disable()后,memory_get_usage()会持续虚高,直到脚本结束才由 OS 回收 - 用
gc_status()查当前 GC 状态:collected字段为 0 表示本轮没回收任何周期
生产环境该信哪个值
监控脚本内存不能只盯 memory_get_usage():
- 查单次请求真实开销 → 用
memory_get_peak_usage(true),它记录脚本生命周期内最高 RSS 估算值 - 查进程常驻内存 → 直接读
/proc/PID/status的RSS字段,或用ps -o pid,rss,comm -p $PID - 排查内存泄漏 → 结合
xdebug_get_memory_usage()(需 Xdebug)、gc_status()和对象引用图分析
最易被忽略的是:memory_get_usage() 在 CLI 模式下行为与 FPM 不同 —— CLI 默认关闭 GC(zend.enable_gc=0),FPM 默认开启。不统一这个配置,对比测试结果毫无意义。



















