当前位置:首页>iOSAPP>iOS 内存管理深度剖析(上):你以为的"内存"其实有 5 种

iOS 内存管理深度剖析(上):你以为的"内存"其实有 5 种

  • 2026-07-15 12:43:58
iOS 内存管理深度剖析(上):你以为的"内存"其实有 5 种

这是 iOS 内存深度剖析系列的第一篇——原理篇。 本系列共三篇:

  • 上篇 · 原理篇(本文):从虚拟内存到 ARC,jetsam 杀进程的真实逻辑。
  • 中篇 · 场景与优化篇:图片解码 / Autorelease 池 / 循环引用 / 大对象生命周期。
  • 下篇 · 实战篇:5 类 OOM 形态 / Memory Graph / MetricKit / 线上止损 Playbook。

同一个 Notification Service Extension,调试时跑得好好的,上线后大量用户反馈"通知图片不显示"。一查崩溃日志全是 OOM——内存才用了 28MB,怎么就被系统杀了?

这是 iOS 工程师最常见的困惑之一。背后有一个反直觉的事实:你看到的"内存",其实是 5 种完全不同的东西混在一起。它们的成本天差地别、被系统看待的方式也不同,但大多数 iOS 工程师从来没把它们分清楚。

这一篇我们就把这 5 种内存讲透——从虚拟内存到物理内存、从 ARC 引用计数到 isa 指针、从 dirty page 到 jetsam 杀进程的真实决策——让你下次看到内存崩溃报告,能立刻知道问题出在哪一层

全文阅读时间约 15 分钟,建议收藏后慢慢看。


一、内存视角:iOS 的"内存"到底指什么

1.1 虚拟内存 vs 物理内存:进程看到的地址全是假的

讨论 iOS 内存之前,先建立一个最重要的认知:你在代码里看到的所有指针、所有内存地址,都是"假地址"——准确说叫虚拟地址(Virtual Address)

NSObject *obj = [NSObject new];NSLog(@"%p", obj);   // 输出 0x00006000017c0040

这个 0x00006000017c0040不是物理内存上的真实位置,而是 iOS 内核给你这个进程分配的"虚拟地址空间"里的一个编号。每个进程都有自己独立的虚拟地址空间——64-bit 系统理论上每个进程的虚拟地址空间能到几百 TB(2^47量级),iOS 实际给用户态进程的范围比这小,但仍远超物理内存能提供的。

但你的设备只有 6GB / 8GB 物理内存,怎么可能给你这么大的虚拟空间?答案是:虚拟地址要通过页表(page table)映射到物理地址才能真正使用

进程视角(虚拟地址空间)              物理内存(实际 RAM)────────────────────────              ─────────────────0x00006000017c0000  ──┐                  ┌─── 真实的 16KB 物理页                      ├── 页表映射 ──────┤0x00006000017c4000  ──┤                  ├─── 另一张物理页                      │                  │0x00006000017c8000  ──X (没映射)         └─── 还没分配

关键认识

  • 进程"看到"的地址全是虚拟的
  • 虚拟地址只有在访问的那一瞬间才会被映射到物理内存
  • 没访问过的地址,物理内存里什么都没有

这个机制是 iOS 内存管理的基石——后面所有内容都建立在这个认知之上。

1.2 一个 App 的内存图谱

打开 Instruments 看一个真实 App 的内存分布,会发现它由很多种"段"组成:

内存类型
来源
典型大小
**__TEXT**(代码段)
App / 动态库的可执行代码
几十 MB ~ 几百 MB
**__DATA**(数据段)
全局变量、ObjC 类元数据
几 MB ~ 几十 MB
Heap(堆)malloc
[NSObject alloc]分配的对象
几 MB ~ 几百 MB
Stack(栈)
每个线程的函数调用栈
主线程 1MB / 子线程 512KB
Mapped File(mmap)
通过 mmap映射的文件,如大图、数据库
看用法
VM Allocatevm_allocate
直接申请的虚拟内存
看用法
Image I/O / IOSurface
ImageIO 框架解码图片产生的位图缓冲
一张 4K 图 ~30-60MB
CALayer Backing Store
UI 图层的渲染缓冲区
屏幕全屏 ~10MB

这些只是用户态可见的部分。内核态还有页表、文件 cache 等,但开发者一般不需要关心。

1.3 用 vmmap 实际看一个 App 的内存分布

最直观的方式是用 vmmap命令(macOS 自带):

# 模拟器 / Mac Catalyst App:直接用 pidvmmap <pid> | head -50

真机上 vmmap受限不能直接调用,几条替代路径:

  • Xcode → Debug → Memory Graph:右下角 Statistics 面板能看到分类内存(最方便)
  • 真机 sysdiagnose:长按音量+电源 5 秒触发,导出后在报告里能找到完整的 vmmap输出
  • Instruments → VM Tracker:可视化版本

输出长这样(这只是简化版本,§4.6 会展示完整版做 footprint 推导):

==== Writable regions for process YourApp [12345]REGION TYPE                      VIRTUAL   RESIDENT   DIRTY__TEXT                            350M       80M       0K     ← 代码段(clean)__DATA                             18M       16M      16Mmapped file                       200M       50M       0K     ← mmap 文件MALLOC heaps                      160M      150M     150M     ← 堆(malloc / new)Image IO / IOSurface               60M       60M      60M     ← 解码图片CoreAnimation                       8M        8M       8MStack                              16M        2M       2M     ← 主线程 + 子线程栈

关键三列

  • VIRTUAL:虚拟地址空间占用(你"申请"了多少)
  • RESIDENT:实际占用的物理内存(真正用了多少)
  • DIRTY:被写过的"脏页"(这个数字才是 jetsam 真正关心的)

反直觉的事实

  • __TEXT段虚拟空间 350MB,但 dirty 是 0——代码段只读,永远不脏
  • mapped file200MB,但 dirty 也是 0——mmap 文件不修改就不脏
  • Stack那行 VIRTUAL=16M 是所有线程的栈虚拟空间总和(每个线程预留 ~1MB / 512KB),RESIDENT=2M 才是实际访问到的——栈只在用到的部分占物理内存
  • 真正贡献内存压力的是 MALLOC和 Image IO这些 DIRTY 高的区域

这就引出了核心结论:内存压力 = dirty page 数量,不是 RESIDENT 总量。这个区分是后面所有内存优化的基础。

记住一句话:决定你的 App 会不会被系统杀的不是"看着用了多少内存",而是 dirty page 有多少。后面 §四 会把"为什么是 dirty"这件事彻底拆开讲。

1.4 iOS 没有传统 swap,但有压缩内存

桌面操作系统(macOS / Linux / Windows)在内存不够时会把不活跃的页写到磁盘 swap 分区,腾出物理内存。但 iPhone 上没有传统 swap——闪存写入次数有限,写入也太慢。

iOS 的替代方案是 memory compressor(内存压缩器)——iOS 7 / macOS 10.9(2013 年)就引入了:

内存压力小:   active page → 留在物理内存内存压力大:   inactive page → 用 WKdm 算法压缩 → 仍留在物理内存                                        (压缩比通常 2:1 ~ 3:1)内存压力极大:   还压不下 → 系统通过 jetsam 杀进程

这是为什么你看到的 RESIDENT 数字有时候比实际"在用"的内存大——一部分已经被压缩了。vmmap输出里的 Compressed那一行就是压缩内存的统计。

iPad 是个例外:iPadOS 16.1 起,搭载 M1 / M2 芯片且 8GB+ RAM 的 iPad 引入了真正的 swap(Apple 称为 "Virtual Memory Swap"),系统按需将不活跃内存写入闪存。iPhone 至今没有 swap。


二、ObjC 内存管理:ARC 不是魔法

ObjC 的 isa / SideTable / weak 机制网上深度文章很多(draveness、sunnyxx 等),重复讲意义不大。这一章只讲三件讲机制时常被遗漏、但和性能直接挂钩的事:① 引用计数到底有多便宜 ② weak 自动 nil 的代价 ③ 一行属性赋值的真实代价。

2.1 引用计数到底有多便宜——nonpointer isa 的精妙之处

很多教程会讲"每个对象都有一个 retainCount 字段"——这是简化但不准确的说法。真实情况:64-bit 时代起,引用计数被压进了 isa 指针的 19 bit 位域里(叫 nonpointer isa)。

为什么能塞进去?因为:

  • arm64 用户态虚拟地址实际只用 47-48 bit
  • 类指针按 8 字节对齐,低 3 bit 永远是 0
  • 所以 64 bit 里能挤出大约 19 bit 空闲位

Apple 把它做成了位域,最关键的字段:

字段
含义
shiftcls
(33 bit)
真正的类指针
has_sidetable_rc
(1 bit)
引用计数是否溢出到 SideTable
extra_rc(19 bit)引用计数(减 1)
——最大 2^19 = 524288
其他
weak / dealloc / 关联对象等单 bit 标志

完整定义在 libobjc 的 objc-private.h。位域 bit 数在不同架构(arm64 真机 / arm64 模拟器 / x86_64)有差异,但设计思想一致。

这个设计的真正性能意义

[obj retain] 的成本:  99% 情况:一次 CAS 原子操作(修改 isa 的 extra_rc 位)→ 几纳秒   1% 情况:引用计数 > 524288,溢出到 SideTable → 加锁 + 查表 → 几百纳秒

也就是说——绝大多数 retain/release 没有锁、不查表。这是 ObjC ARC 在 iOS 这种单核时代过来的语言里仍然能保持高性能的根本。

2.2 SideTable 与 weak 的自动 nil

SideTable 是什么:当 isa 的 19 bit 不够时(极少见)、或者需要存 weak 引用列表时,runtime 会把数据外置到一个全局哈希表 SideTable——按对象地址哈希到 64 张表中的一张(分散降锁竞争)。

weak 指针自动置 nil 的实现也在这里

  • __weak赋值时,runtime 把 weak 指针的地址记到对应对象的 weak_table 里
  • 目标对象 dealloc 时,runtime 遍历 weak_table,把所有指向自己的 weak 指针逐一写为 nil

性能代价

  • __weak赋值比 __strong慢一个数量级(要查表 + 加锁)
  • 在循环里频繁创建 weak 引用是反模式——例如 for cell in cells { __weak typeof(self) wself = self; ... }wself应该提到循环外只创建一次

2.3 一行 self.obj = newObj的真实代价

ARC 的本质是编译器自动帮你插 retain/release 代码——它是编译期机制,不是运行时机制。

你写的:

self.obj = newObj;

编译器实际展开为:

objc_storeStrong(&self->_obj, newObj);// 内部:objc_retain(newObj) → 写指针 → objc_release(prev)

真实成本:1 次原子 retain + 1 次指针赋值 + 1 次原子 release。

如果 release 后引用计数归 0,还会链式触发完整的 dealloc 流程:

  1. 调用对象自己的 -dealloc
  2. 释放该对象的所有 strong ivar(每个又触发一次 release——可能递归)
  3. 通知 weak_table 把所有 weak 引用置 nil
  4. 通知关联对象表清理
  5. 沿继承链调用父类的 dealloc(直到 NSObject)
  6. 调 free把内存归还给分配器

实战结论:在性能敏感的代码里——比如列表 cell 的 layoutSubviews、动画的每帧回调——减少不必要的 strong 属性赋值能带来可观的性能收益。这是大多数"ObjC 源码分析"文章会漏掉的实操结论:他们只讲机制,不讲"机制对你的代码意味着什么"。

记住一句话:ARC 不是运行时机制——它是编译器在你看不到的地方插了 retain/release 代码。你写得越简洁,它替你插得越多。self.obj = newObj是 3 次操作,不是 1 次。


三、Swift 内存管理:与 ObjC 同根但不同枝

Swift 的内存管理本质上和 ObjC 是同一套机制——都是 ARC + 引用计数。但它在三个地方有显著不同。

3.1 Swift 的引用计数:strong / weak / unowned

ObjC 只有 strong和 weak,Swift 多了一个 unowned

关键字
引用计数
目标释放后访问
适用场景
strong
+1(strong)
N/A(持有,不会释放)
默认
weak
不增加 strong
自动变 nil(必须 Optional)
可能被释放的引用
unowned
不增加 strong(维护一份 unowned 计数)
运行时错误
(safe 版)/ UB(unsafe 版)
生命周期保证不会比目标长

Swift 的 unowned默认是 unowned(safe)——仍维护一份轻量的 unowned reference count,访问已释放对象时 runtime 能检测到并抛错(不会任意访问垃圾内存)。如果用 unowned(unsafe)才是真正的裸指针,性能更好但完全不安全。

unowned(safe)比 weak性能更好——不需要维护 weak_table、不需要 Optional 解包——但用错就崩溃。常见安全用法:

classPerson{let name: Stringlazyvar greet: () -> String = { [unownedselfinreturn"Hello, \(self.name)"// self 一定存在,因为闭包是 self 的属性    }init(name: String) { self.name = name }}

实操建议:90% 场景用 weak,只在确信生命周期的关键热点路径用 unowned

3.2 Value type 的"没有引用计数"假象

Swift 的 structenumtuple是 value type——赋值是复制,没有引用计数。但有个反直觉的事实:

Swift 的 Array / Dictionary / String / Set 都是 struct,但内部用了引用计数 + COW(Copy-On-Write)

var a = [12345]   // a 持有一个引用计数为 1 的 buffervar b = a                 // 没有复制!b 和 a 共享同一个 buffer,引用计数变为 2b.append(6)               // 触发 COW:检测到旧 buffer 引用计数 > 1// → 给 b 复制一份新 buffer// → b 释放对旧 buffer 的引用// 最终:a 持有旧 buffer(计数 1)、b 持有新 buffer(计数 1)

这意味着

  • let a = bigArray; let b = a看起来是值类型复制——实际不复制
  • 一旦其中一个被修改,才真正复制
  • 这个判断用 isKnownUniquelyReferenced(&buffer)在 runtime 完成

性能特征

  • 读多写少的场景:和引用类型一样快,零拷贝
  • 多人共享 + 高频写入:每次写入前都要做一次 isKnownUniquelyReferenced检查,遇到共享时复制 buffer——这是为什么大型 SwiftUI 项目里偶尔出现 "Array 操作慢" 的根因

3.3 ARC + Generics + Existential 的特殊性能特征

这一节相对底层,对日常开发不感兴趣的可以跳过——只需要记住结论:"性能热点路径上,泛型比协议类型快"。

Swift 比 ObjC 多了一类内存开销:Existential Container(存在性容器)。当你把一个值类型装进协议类型时,Swift 需要一个固定大小的容器来存它:

protocolDrawablefuncdraw() }structCircleDrawablevar x, y, r: Doublefuncdraw() { /* ... */ } }let circles: [Drawable] = [Circle(x: 0, y: 0, r: 1)]//   ^^^^^^^ 这里发生了什么?

[Drawable]数组里每个元素必须固定大小(因为数组要按 index 计算偏移),但 Circle是 24 字节、其他实现 Drawable的类型可能更大。Swift 用 Existential Container解决这个问题(单协议存在性容器):

Existential Container(5 word = 40 字节,arm64)┌─────────────────────────────────────────────────┐│ Inline storage (3 word = 24 字节)               │ ← 不超过 24 字节直接存这里│ Type metadata pointer (1 word = 8 字节)         │ ← 类型元数据│ Witness table pointer (1 word = 8 字节)         │ ← 协议方法的虚函数表└─────────────────────────────────────────────────┘

Witness table 可以理解为"协议方法的虚函数表"——每个实现该协议的类型都有一份,记录了协议要求的方法实现地址。

  • 不超过 24 字节的值Circle三个 Double 正好 24 字节):直接 inline 存储,零开销
  • 大于 24 字节的值:堆分配一个 buffer,container 里存 buffer 指针——额外的堆分配 + 引用计数

性能影响

// 性能不好:每个元素都要走 Existential Container,大对象还要堆分配let shapes: [Drawable] = [...]shapes.forEach { $0.draw() }   // 每次调用走 witness table 间接分发// 性能好:泛型在编译期被特化(specialization)——编译器为每种 T 生成专门的代码funcdrawAll<T: Drawable>(_ items: [T]) {    items.forEach { $0.draw() }   // 直接调用,无虚分发}

实操建议:性能敏感的代码用泛型而不是协议类型——在热点路径上能换来 2-5 倍性能提升。

记住一句话:Swift 的引用计数比 ObjC 少了 SideTable 哈希查找的开销,但多了 Existential Container 这个"装协议类型的容器"——前者是优化,后者是新增成本。


四、5 类内存页:dirty / clean / compressed / swap / mmap

这一章是后面所有内存优化的基础——读者如果只看一章,建议看这章。

4.1 五类内存页的成本对比表

iOS 上的内存页按"成本"可以分成 5 类:

类型
物理内存占用
何时产生
内存压力下的命运
例子
dirty
✅ 占用
被写过
必须保留(除非压缩或杀进程)
malloc 的对象、栈、可写全局变量
clean
⚠️ 占用但可丢弃
从文件 page-in 但未修改
直接丢弃
,要用再读回
__TEXT
代码段、__DATA_CONST常量段
compressed
部分占用(压缩后)
dirty page 被压缩
压缩留在内存
不活跃的 dirty page
swap
(仅部分 iPad)
0(已写入闪存)
compressed 仍然不够时
写入闪存释放物理内存
iPad Pro 8GB+ 才有
mmap
(mapped file)
✅ 占用但 dirty=0
用 mmap映射文件
当作 clean page 处理(除非写入)
大图直接 mmap、SQLite

三个反直觉的结论

  1. __TEXT段(代码)几百 MB 不算内存压力——它是 clean,系统压力大就丢,再要用从磁盘读回
  2. mmap 一个 200MB 大文件不算内存压力——只要不写入它
  3. 真正贡献 jetsam 杀进程概率的是 dirty page——这是 iOS 内存优化的核心目标

4.2 dirty page 是 iOS 杀进程的核心指标

为什么 dirty page 是关键?因为它不能被丢弃。这是和 clean page 最本质的区别:

clean page:物理内存不够 → 丢掉它 → 数据还在磁盘 → 要用时从磁盘读回dirty page:物理内存不够 → 丢不掉  → 必须压缩 / 写 swap / 杀进程

iOS 内核在内存压力大时,会按这个顺序处理:

① 释放 clean page(free 即可,无副作用)   ↓ 还不够② 压缩 inactive 的 dirty page(compressed memory)   ↓ 还不够③ 写入 swap(仅高端 iPad)   ↓ 还不够④ 触发 jetsam,杀掉一个进程释放它的所有 dirty page

这就把"footprint"这个核心指标解释清楚了

  • Crash 报告里的 "Footprint"≈ dirty page + compressed 之和
  • 30MB Extension 内存上限,限制的就是 footprint
  • 优化内存的第一目标:减少 dirty page

4.3 clean page 几乎免费

clean page 的典型例子是 __TEXT——你的 App 二进制里的代码,被 mmap 进内存,永远只读、永不修改。下篇 §4.6 会展示完整 vmmap 输出——你会看到 __TEXT那行 RESIDENT 几十甚至上百 MB,但 dirty 永远是 0。

这就是为什么大型 App(QQ 几百 MB 二进制)启动后看着内存占用很大,但实际不会因此被杀

Apple 在这点上做了大量优化

  • __DATA_CONST段:fixup 后变只读,从 dirty 转为 clean
  • __DATA_DIRTY段:明确标识"这一块永远是 dirty 的",方便监控
  • dyld_shared_cache:所有 App 共享同一份系统库的 clean 内存映射

4.4 compressed memory:iOS 的"软 swap"

iOS 7 起内置的 memory compressor 工作机制:

进程 A 的 dirty page:   active 时:留在物理内存(占 16KB)   inactive 后:用 WKdm 压缩 → 占 4KB-8KB(仍在物理内存)   再被访问时:解压缩 → 重新变成 16KB dirty page效果:   原来 100MB dirty → 实际占用 35-50MB   压缩比一般 2:1 ~ 3:1

对开发者的影响

  • App 在后台时间长了,dirty 会逐渐变成 compressed(看起来 RESIDENT 在降)
  • 切回前台访问时会有"解压缩成本"——多数 App 几毫秒到几十毫秒,大型 App 用户能感觉到一瞬间的卡顿
  • compressed 仍然算进 footprint,仍然会让你被 jetsam 杀

4.5 mmap 文件:既不是 dirty 也不是 clean

mmap是个特殊存在——它把磁盘文件映射到虚拟地址空间:

int fd = open("big_file.dat", O_RDONLY);void *ptr = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);// 现在 ptr 指向的虚拟内存就是文件内容

关键特征

  • 读取时:触发 page fault,从磁盘读 16KB 到物理内存(clean page
  • 不读取时:不占物理内存
  • 写入时(如果是 MAP_PRIVATE):触发 COW,复制一份变成 dirty page

实战用法

  • 大图直接用 mmap加载——只占虚拟地址空间,物理内存按需读
  • SQLite 的 WAL 模式底层就是 mmap
  • Data(contentsOf: url, options: .alwaysMapped)也是 mmap

陷阱mmap一个超大文件不会立刻 OOM,但遍历访问它会让所有页都被 page-in 进物理内存,瞬间内存暴涨。

4.6 实战:算一算你 App 的真实 footprint

理解了 5 类页之后,我们用一个真实场景把它们串起来——你 App 在 Xcode Memory Debug 里看到的"200 MB",到底有多少会被 jetsam 算进 footprint

假设一个中型 App 的 vmmap 摘要长这样:

REGION TYPE                      VIRTUAL   RESIDENT   DIRTY    SWAPPED__TEXT                            350M       80M       0K       0K__DATA_CONST                       40M       38M       0K       0K__DATA                             18M       16M      16M       0Kmapped file                       200M       50M       0K       0KMALLOC heaps                      160M      150M     150M       0KImage IO / IOSurface               60M       60M      60M       0KCoreAnimation                       8M        8M       8M       0KStack                              16M        2M       2M       0KCompressed                          0K       30M      30M       0K─────────────────────────────────────────────────────────────────Sum                              ~852M     ~434M    ~266M       0K

Xcode 顶部显示的 "Memory: 434 MB"≈ RESIDENT 总和。但这个数字不是 jetsam 真正看的

真实 footprint 的算法(简化版):

footprint = 各行的 dirty 之和 + Compressed         = 16M[__DATA] + 150M[MALLOC] + 60M[ImageIO] + 8M[CoreAnimation]           + 2M[Stack] + 30M[Compressed]         = 266M

也就是说

  • Xcode 看到 434MB,但只有 266MB 计入 footprint
  • 其中 __TEXT80MB / __DATA_CONST38MB / mapped file50MB 完全不算——它们是 clean,系统压力大就丢掉
  • Compressed30MB 要算进去——压缩了仍然是 dirty 的"另一种形态"

这解释了几个常见困惑

  1. "为什么我减小图片资源 App 占用没降?":图片资源放在 Bundle 里,加载时是 clean page(mmap),减它对 footprint 没影响——除非你减的是运行时解码后的位图(IOSurface)。

  2. **"为什么大型 App(QQ、微信)二进制几百 MB 还能跑?"**:因为 __TEXT几乎全是 clean,不计入 footprint。这就是为什么文章上一节 "QQ 几百 MB 二进制启动后实际不会因此被杀"——现在你看到了完整推理。

  3. "为什么后台 App 看起来用了 100MB 还是被杀了?":因为后台 App footprint 上限只有 ~50-200 MB,你 100MB 全是 dirty,已经接近红线

实操方法:怎么算自己 App 的真实 footprint?

# 1. 模拟器 / Mac 上:直接 vmmap 看vmmap <pid> -summary# 2. 真机上:sysdiagnose 报告里的 vmmap 输出#    或:Xcode Memory Graph → Statistics 面板里直接显示 footprint# 3. 代码里实时获取(推荐):#import <mach/mach.h>task_vm_info_data_t info;mach_msg_type_number_t count = TASK_VM_INFO_COUNT;kern_return_t kr = task_info(mach_task_self(), TASK_VM_INFO,                              (task_info_t)&info, &count);if (kr == KERN_SUCCESS) {    NSLog(@"Footprint: %llu MB", info.phys_footprint / 1024 / 1024);    //                              ↑ 这就是 jetsam 看的那个数字}

phys_footprint是关键——它是内核暴露给用户态的"真正会被 jetsam 算账的内存"指标,iOS 9+ 起在 TASK_VM_INFO里可以直接读取。所有正经的内存监控 SDK(OOMDetector、FBMemoryProfiler 等)都是基于这个 API 做的。

记住一句话:5 类页里,只有 dirty 和 compressed 真正贡献"被杀概率"——clean / mmap-未写 / 系统能丢的都不算数。所以"减少内存"的真正含义是"减少 dirty page",而不是"减少看起来的内存数字"——phys_footprint才是你应该监控的指标。


五、jetsam:iOS 杀进程的真实逻辑

很多人以为"App 用了太多内存就被杀"——这话不全错,但远没那么简单。jetsam是 iOS 内核的内存压力守护机制,它的决策远比"超限就杀"复杂。

5.1 jetsam 的优先级表

iOS 把所有进程按优先级分档(XNU 内核里的 JETSAM_PRIORITY_*常量):

常量
数值(参考)
例子
JETSAM_PRIORITY_IDLE
0
完全空闲的进程
JETSAM_PRIORITY_BACKGROUND
3
你的 App 在后台
JETSAM_PRIORITY_AUDIO_AND_ACCESSORY
9
后台播音乐、连配件的 App
JETSAM_PRIORITY_FOREGROUND10前台 App
JETSAM_PRIORITY_CRITICAL
19
关键系统进程(SpringBoard 等)

具体数值会随 iOS 版本微调,但相对顺序稳定。可以参考 XNU 源码里的 kern/kern_memorystatus.h

内存压力来时,jetsam 从优先级数值最低的开始杀——优先级相同时,按"占内存多少"决定。这就是为什么你的 App 在后台被杀的概率远大于前台。

5.2 内存压力的三档信号

iOS 用三档信号通知系统当前的内存压力:

正常运行   │   ▼DISPATCH_MEMORYPRESSURE_NORMAL    ← 没事   │   ▼ 内存开始紧张DISPATCH_MEMORYPRESSURE_WARN      ← 警告(低优先级进程开始被杀)   │   ▼ 内存极度紧张DISPATCH_MEMORYPRESSURE_CRITICAL  ← 严重(前台进程都可能被杀)

App 可以监听这些信号,在收到 WARN 时主动释放缓存:

// 假设 ImageCache 是你自己实现的图片缓存类let source = DispatchSource.makeMemoryPressureSource(    eventMask: [.warning, .critical],    queue: .main)source.setEventHandler {if source.data.contains(.critical) {// 紧急释放:清空所有缓存、放弃非关键操作ImageCache.shared.removeAll()    } elseif source.data.contains(.warning) {// 警告:清理一半缓存ImageCache.shared.trim(toLowerLimit: 50)    }}source.resume()

实战价值:及时响应这些信号,能让你的 App 在内存压力下比别人晚被杀——优先级一样的情况下,jetsam 会先杀占内存多的。

5.3 内存上限不是固定值

很多人以为"iPhone 8GB RAM,App 能用 8GB"——错。iOS 给每个 App 设了 footprint 上限,超过就直接杀(不管整机内存多富裕)。

这个上限动态决定,影响因素:

因素
影响
设备总 RAM
6GB 设备 ~3GB 上限;8GB 设备 ~4-5GB 上限
App 状态
前台 > 后台 > Extension
是否打开 Increased Memory Limitentitlement
(iOS 15+)
高内存设备可申请更高上限
当前系统内存压力
压力大时上限会被动态压缩

参考经验值(iPhone 14 Pro,6GB RAM):

  • 前台 App:~2-3 GB
  • 后台 App:~50-200 MB
  • Today Widget / Notification Service Extension:24 MB
  • WidgetKit Widget:30 MB

Extension 内存上限在你启动系列中篇 §1.1 已经讲过——为什么这个数字这么小,根因就是这一节。

5.4 前台 / 后台 / Extension 的限额对比

设备总 RAM 6GB├─ 内核 + 系统服务      ~1.5 GB├─ SpringBoard 等          ~300 MB├─ 前台 App 上限          ~2-3 GB     ← 你的 App├─ 后台 App(每个)       ~50-200 MB└─ Extension              24-50 MB

Extension 的上限为什么这么严格?因为它是临时进程——系统希望它快进快出,不能像主 App 那样常驻。这就是你启动系列中篇强调"Extension 不能 link 主 App 全家桶 framework"的根因。

这里也解答了开头那个问题——Notification Service Extension 跑到 28MB 就被杀,正是因为它的 footprint 上限只有 24MB(见 §5.3)。Extension OOM 在生产环境里比主 App 崩溃更难定位——下篇会专门讲。

5.5 Crash 报告里识别 jetsam

当 App 因内存被杀时,Crash 报告主要有两种形态。

形态一:单进程超限(per-process-limit)——你的 App 自己用太多了:

Exception Type:    EXC_RESOURCEException Subtype: MEMORY (per-process-limit)Termination Reason: PER_PROCESS_LIMITMemory Footprint: 50 MB    ← 实际内存占用Memory Limit:     50 MB    ← 上限

形态二:系统压力下被 jetsam 杀——你没超限,但系统整体内存吃紧:

Termination Reason: Namespace SIGNAL, Code 0x9 (SIGKILL)Termination Description: jettisoned ...

关键识别要点

  • EXC_RESOURCEMEMORY子类型 = 形态一(单进程超限,你自己的问题)
  • 含 jettisoned或 MEMORY关键字、且没有完整业务调用栈 = 形态二(系统压力下被牺牲)
  • 两种形态 Crash 报告里都没有传统的崩溃栈——因为是被外部 SIGKILL 的,不是自己挂的

具体字段会随 iOS 版本而变。识别 OOM 主要靠两个信号:① Termination Reason / Description 里出现 MEMORYjettisonedper-process-limit;② 没有可用的崩溃调用栈。

启动期就被 OOM 杀的情况,参见启动系列下篇 §1.1 五类 launch crash。

记住一句话:jetsam 不是"超内存就杀",而是"按优先级 + footprint 排队,从最弱的开始杀"——这就是为什么 Extension 限额最严、后台 App 最容易被回收、前台 App 相对安全。


上篇小结

到这里我们已经走完了 iOS 内存的"原理面":

  • 虚拟内存让你看到的所有地址都是假的,真正的物理内存按需分配
  • ObjC ARC不是魔法——nonpointer isa 让 99% 的 retain/release 只是位运算,1% 才走 SideTable;编译器在你写的每行属性赋值背后插了 retain + 写指针 + release 三步
  • Swift同根但不同枝——多了 unowned、Existential Container、Value Type 的 COW
  • 5 类页中,dirty + compressed才是真正贡献内存压力的部分——phys_footprint才是你该监控的数字,不是 RESIDENT 总和
  • jetsam按优先级杀进程,每个 App 有动态的 footprint 上限,Extension 限额最严

记住一句话:iOS 内存优化的本质,是减少 dirty page 数量——不是减少 RESIDENT 总量、不是减少虚拟空间。

中篇我们继续讨论:

  • 图片凭什么是 iOS 内存第一杀手?UIImage(named:) 的隐性 cache 怎么破?
  • Autorelease Pool在子线程不包会怎样?一个 for 循环怎么从 200MB 压到 20MB?
  • 循环引用在 Combine / Swift Concurrency 时代有哪些新型陷阱?
  • NSCache vs 自实现 LRU,到底应该用哪个?

中篇见。


参考资料:Apple WWDC 2018《iOS Memory Deep Dive》、WWDC 2021《Detect and diagnose memory issues》、libobjc 源码、Mike Ash《Friday Q&A》系列。

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-07-17 06:38:45 HTTP/2.0 GET : https://c.mffb.com.cn/a/491061.html
  2. 运行时间 : 0.341671s [ 吞吐率:2.93req/s ] 内存消耗:4,451.32kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=ba358daf0b1eb5aaf124379e7d4d17c6
  1. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/runtime/temp/cefbf809ba1a84190cb04b0cb7abcf79.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000460s ] mysql:host=127.0.0.1;port=3306;dbname=c_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000605s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.003524s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.006463s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000543s ]
  6. SELECT * FROM `set` [ RunTime:0.000741s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000618s ]
  8. SELECT * FROM `article` WHERE `id` = 491061 LIMIT 1 [ RunTime:0.000558s ]
  9. UPDATE `article` SET `lasttime` = 1784241526 WHERE `id` = 491061 [ RunTime:0.016278s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 65 LIMIT 1 [ RunTime:0.004580s ]
  11. SELECT * FROM `article` WHERE `id` < 491061 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.005478s ]
  12. SELECT * FROM `article` WHERE `id` > 491061 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.001827s ]
  13. SELECT * FROM `article` WHERE `id` < 491061 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.029045s ]
  14. SELECT * FROM `article` WHERE `id` < 491061 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.118727s ]
  15. SELECT * FROM `article` WHERE `id` < 491061 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.086766s ]
0.343205s