
线上 Java 崩溃里 OOM 的占比一直不低。我们去年下半年拉过一次数据OOM 加上跟内存相关的 ANR 大概占到两成出头但真正能从崩溃栈里看出泄漏点的不到一成——因为 OOM 的堆栈永远指向那次失败的 allocation而不是那个忘了注销的监听器、那个没解绑的 View、那个静态 Map 里越堆越多的 Context。要定位这类问题只有两条路在本地把它复现出来或者在线上把堆的状态带回来。LeakCanary 是第一条路上事实上的标准工具KOOM 是第二条路上工程化程度比较高的一个方案。这篇就把这两者摊开对比重点讲清楚一个问题为什么在“线上监控”这个场景里我们把主力从 LeakCanary 换成了 KOOM以及换完之后省下了什么、又付出了什么代价。1. 先把“泄漏检测”和“线上监控”拆成两件事很多人做技术选型时会把这两个词混着用导致对比维度从一开始就错位。LeakCanary 和 KOOM 都能告诉你“这里有个泄漏”但它们被设计出来要解决的问题并不在同一个层面上。搞清楚这个差别后面的取舍就都是顺理成章的。1.1 LeakCanary 的原始目标让开发者在工位上把问题看明白LeakCanary 的核心机制其实很朴素用一组带 key 的弱引用去盯住那些理论上应该被回收的对象配合ReferenceQueue感知它们什么时候被真正回收。如果一个 Activity 销毁后过了设定的观察窗口默认 5 秒还没被回收就把它标记成“疑似 retained”再叠加上一个阈值默认累积到 5 个疑似对象才真正触发一次堆转储。转储之后用 Shark 解析 hprof找出从 GC Root 到这个对象的最短强引用链最后以一条人能读懂的 Leak Trace 呈现出来。这套设计的每一个环节都指向同一个目标信息完整度优先。它要给你的是“谁持有谁、为什么没断开”的完整链路所以你打开通知就能直接去改代码。为了这个目标它愿意付出几秒钟的卡顿、愿意在设备上生成一个几百 MB 的 hprof 文件、愿意在设备上跑一遍相当吃 CPU 和内存的分析。在开发机上、在测试同学的手机上这些代价完全值得。1.2 线上监控要的是另外三样东西线上环境里“完整”反而是最不重要的。真正卡住你的是这三件事第一是低打扰。用户不会因为你正在做内存诊断就容忍一次两秒的白屏。哪怕只是一次滑动掉帧在核心页面上的代价也可能被放大成一次流失。第二是可聚合。线上一次灰度出去同一处泄漏可能在几千台设备上同时发生。你需要的是“这是一个问题影响了 1200 台设备集中在某个版本”而不是收到 1200 份互相独立、各写各的的堆分析报告。第三是可归因。线上拿到的数据必须是能对上版本的哪个 app 版本、哪个机型、哪个系统版本、哪个业务入口。脱离这些维度的泄漏报告修起来依然要靠猜。这三条要求恰好是 LeakCanary 的默认形态最不擅长的地方。它的架构里堆转储和分析全都在业务进程内完成数据也不落库、不上报天然就是“单机、即时、面向个人”的工具。1.3 真正的分岔点在两个位置dump 在哪执行、分析在哪执行把这两个工具的所有差异归结起来其实就是两个坐标坐标LeakCanaryKOOM堆转储dump执行位置业务进程内fork 出的子进程堆分析执行位置业务进程内Shark服务端/离线端上只做裁剪只要这两件事都发生在业务进程里无论怎么优化参数你都无法绕开“暂停时间长、内存峰值高、数据带不走”这三个物理限制。反过来只要把 dump 挪出主进程、把分析挪出设备剩下的问题就都变成了工程问题而不是原理问题。KOOM 的价值基本上就在这两次“挪动”上。顺带说一句LeakCanary 官方也意识到了线上场景的需求后续版本提供了面向 release 包的能力可以按需触发 dump 并接入自定义的分析服务。这个方向是对的但它的底座仍然是进程内 dump最耗时的那个环节并没有变。所以我的判断是开发期用 LeakCanary线上用 KOOM这不是二选一而是分工。2. 把 dump 搬到子进程KOOM 的 fork 机制到底省下了什么要理解 KOOM 为什么快得先知道一次堆转储的时间到底花在哪。很多人以为 dump 就是把内存“复制一份”实际上完全不是——它是一次全堆遍历加序列化是相当重的操作。2.1 一次堆转储的时间账本以 ART 为例一次完整的 hprof dump 大致要做这些事挂起所有线程SuspendAll。这一步本身很快但必须等到所有线程都到达安全点如果有线程正在执行长循环或者持有锁父进程就得等。遍历堆。从各个 SpaceRegionSpace、ImageSpace、ZygoteSpace、LargeObjectSpace 等逐个扫描对象读取对象头、类信息、字段值。一个中等规模 App 的存活对象数量在几十万到上百万量级。序列化写文件。每个对象都要按 hprof 格式写出 tag、id、类名、字段还要处理字符串去重、类结构记录、GC Root 记录。这一步是纯 IO 加 CPU。恢复所有线程ResumeAll。这里面的时间跟堆大小基本是正相关的。粗略的量级感受是一个堆占用一两百 MB 的进程中端机型上一次完整 dump 的挂起时间在两秒上下堆更大的话三到五秒也不稀奇。而且这期间不只是卡顿进程的 RSS 还会明显上涨——因为要构造输出缓冲、要写文件物理内存占用会往上冲一截。这就解释了为什么很多团队试过把 LeakCanary 直接放到线上灰度包里跑了一两天就撤了崩溃率没降ANR 和卡顿投诉先上来了。2.2 fork 加写时复制把“写”这件事挪出主进程KOOM 的核心手法是fork()。这个系统调用会创建一个子进程子进程的地址空间在逻辑上跟父进程一模一样但物理内存页是共享的——只有当某一方尝试写入某个页时内核才会真正复制一份出来这就是写时复制Copy-on-Write。于是整个流程可以变成这样父进程先SuspendAll保证此刻堆的状态是一致的、不会有一半的对象刚分配一半没填完父进程fork()出子进程然后把子进程的地址空间视图继承过去父进程立刻ResumeAll业务线程继续跑用户感知到的停顿只有挂起和 fork 这两步量级在百毫秒以内甚至更低子进程慢慢去做遍历、序列化、写文件这些脏活累活。它在写的过程中会不断触发缺页复制但那部分是子进程自己的内存开销跟业务进程的响应性没关系。这个设计最妙的地方在于它把“耗时”和“阻塞”解耦了。总耗时可能并没有变短甚至因为 COW 的缺页开销还会略微变长但业务线程被阻塞的时间从秒级降到了百毫秒级。对线上监控来说这才是关键指标。需要强调的是挂起那一步不能省。如果在没挂起的情况下 fork父进程的堆还在变子进程拿到的就是一份撕裂的快照——可能出现指针指向一个还没构造完的对象遍历的时候直接崩。所以“短挂起 fork”是一个整体不能只取一半。2.3 在子进程里调用 ART 的那些坑原理讲起来很顺真做起来最麻烦的地方在于fork 出来的子进程地址空间虽然一样但线程只有一个调用 fork 的那个。而 ART 的 dump 逻辑内部依赖大量的锁mutator lock、heap lock、各种 monitor。如果 fork 的那一刻父进程里某个线程正持有某把锁而那个线程在子进程里并不存在那么子进程去拿这把锁就会永远等下去直接死锁。这个问题没有特别优雅的解法工程上的处理思路大致是在挂起所有线程之后再 fork这样所有业务线程都停在安全点锁的持有状态相对可控子进程里只允许跑一个线程避免自己跟自己抢锁针对不同 Android 版本的 ART 实现做适配因为 5.x、6.x、8.0、10 之后在堆结构和 hprof 格式上都改过尤其是新版本 hprof 的头部和记录格式变化在 dump 之前先做一些“探针”性质的检查比如堆是否处于可转储状态、可用磁盘空间是否够避免做到一半失败还留下半个文件。还有一个容易被忽略的限制是地址空间。32 位进程的虚拟地址空间只有几个 G堆本身已经占了一大块fork 再加写 hprof 的缓冲很容易直接失败。所以这套方案基本只在 64 位进程上开启32 位设备上一般直接降级或者不监控。这不是妥协是物理限制。2.4 暂停时长与失败率的实际观感从我们自己的体感来说从“进程内 dump”切到“子进程 dump”之后最明显的变化是用户侧的可观测异常基本消失了。之前灰度包里偶尔能收到“应用无响应”的反馈切过去之后这类反馈归零。挂起时间从原来的秒级降到了百毫秒级这个量级在大多数页面上用户是感知不到的但如果你正好在做列表惯性滑动理论上还是可能有轻微掉帧所以比较稳妥的做法仍然是把触发时机放在页面切换、退到后台、或者应用空闲的时候。代价是失败率。子进程 dump 在各版本 ART 上的兼容性不如进程内 dump 那么稳尤其是在一些深度定制的系统上偶发会出现子进程异常退出、dump 文件不完整的情况。所以接入的时候一定要有兜底dump 失败就放弃这一轮不要重试不要降级到进程内 dump。重试会让问题雪上加霜而降级到进程内 dump 等于把好不容易省下来的卡顿又还回去了。等下一轮监控周期再说本来线上监控就是个长期采样的事情不差这一次。3. 逐项对照八个真正影响决策的维度把机制讲清楚之后剩下的就是把这些差异翻译成选型时能直接用的判断依据。我按重要性排了一下下面这张表是我们内部评审时实际用过的一版。维度LeakCanary默认形态KOOM对线上监控的影响运行环境默认只在 debug 变体生效面向 release 设计KOOM 可以直接跟着灰度走dump 位置业务进程内fork 子进程决定卡顿量级挂起时长秒级百毫秒级决定用户是否可感知内存峰值高易被系统回收峰值转移到子进程低端机存活率差别明显分析位置设备端 Shark服务端/离线决定能否跨设备聚合单次数据量完整 hprof几百 MB裁剪后的引用链几十 KB决定上报成本能否承受覆盖范围以 Java 堆对象为主Java 堆、Native 堆、线程、FD决定能发现多少类问题采样能力无内置采样概念支持采样与阈值配置决定线上打扰面3.1 接入成本与构建变体LeakCanary 的接入体验几乎是无敌的加一行debugImplementation什么都不用配跑起来就能用。这个“零配置”是它最大的优势也是它被牢牢锁在开发期的原因——它的默认假设就是“你只在本地用”。KOOM 的接入要重一些。除了依赖本身的体积你还需要考虑初始化时机、采样配置、上报通道、服务端解析链路如果要用 Native 泄漏监控还得处理符号表。粗略估算一个完整的接入从零到能看到第一份线上报告工作量大概在两到三天的量级前提是服务端的解析链路已经有人搭好了。所以这里的判断标准很简单如果你的诉求只是“开发阶段别写出泄漏”LeakCanary 一行依赖就够了如果你要的是“线上到底哪些页面在漏、漏得有多严重”那这点接入成本是必须付的。3.2 运行期开销CPU、内存峰值与卡顿LeakCanary 的开销集中在两个瞬间dump 的那几秒以及 Shark 分析的那十几秒到几十秒。分析阶段的内存峰值尤其要命因为它需要在内存里构建对象图、算支配树、找最短路径这些数据结构本身的体积跟堆里对象数量是同一量级的。在一些内存本身就吃紧的机器上很容易出现“为了查泄漏把自己搞成 OOM”的尴尬局面。KOOM 把这两段开销拆开了dump 的开销转移到子进程分析的开销转移到服务端。业务进程承担的主要是挂起那一小段以及 fork 本身带来的页表复制开销。这个开销跟进程的虚拟内存大小有关进程越大越明显但总体仍然是可控的。这里有个实践中的细节值得提一句不要在应用启动阶段触发 dump。启动阶段本来就是内存和 CPU 的高峰期这时候 fork 一个子进程页表复制的代价会被放大而且这个阶段的堆状态最不稳定。把触发窗口往后放等首屏渲染完成、用户进入稳定态之后再考虑。3.3 数据体积与隐私边界这一条经常被低估但在真实项目里往往是决定性的。LeakCanary 生成的是完整 hprof体积跟堆大小成正比几百 MB 是常态。这种东西在本地看看没问题往上一传就是灾难用户的带宽、服务端的存储、解析的算力三样都要钱。更要紧的是完整 hprof 里包含着进程内几乎所有的字符串——用户输入的文本、接口返回的内容、图片的 URL全都躺在里面。把它原样上传隐私风险是实打实的。KOOM 的处理方式是端上裁剪在子进程里 dump 出 hprof 之后先在本地解析一遍只保留从 GC Root 到疑似泄漏对象的那条引用链上涉及的对象和类信息其余全部丢弃。最终上报的数据量通常在几十 KB 的量级只有全量 hprof 的千分之一甚至更少。这个裁剪带来的一个副作用是信息损失——你拿不到完整的堆快照也就没法做“全堆扫描”式的分析比如查某类对象的实例总数。但对“定位泄漏点”这个目标来说引用链本来就是最有信息量的部分损失可以接受。3.4 分析能力设备端精读与服务端归并Shark 是一个相当成熟的分析引擎它能算支配树、能找最短强引用路径、能识别常见的已知泄漏模式而且它的输出是给人看的——Leak Trace 会把整条链路用缩进的形式展开哪一层是系统类、哪一层是业务类标得清清楚楚。在开发期这个体验目前没有对手。KOOM 在端上不做深度分析只做裁剪和上报真正的分析在服务端完成。这意味着单看一份报告它的呈现可能不如 Leak Trace 那么友好。但服务端分析换来的是另一个维度的能力归并。当同一处泄漏在 500 台设备上发生时服务端可以按引用链的哈希把它们聚成一条记录告诉你“这个问题影响了 500 台设备、集中在 3.2.1 版本、TOP3 机型是这几款”。这个视角是设备端分析给不了的。3.5 覆盖范围Java 堆之外还有多少坑线上 OOM 里Java 堆溢出只是一部分。实际遇到的还有Native 内存泄漏C/C 层 malloc 之后没 freeJava 堆看起来很正常进程 RSS 一路涨。线程泄漏不断创建线程不回收最后pthread_create失败抛出来的是一个看起来莫名其妙的 OOM。FD 泄漏文件描述符耗尽报错形式五花八门。LeakCanary 的主战场在 Java 堆上的 Activity、Fragment、ViewModel、Service、View 这些对象。KOOM 则把这几类都覆盖了Java 泄漏靠子进程 dumpNative 泄漏靠 hook 内存分配函数做采样记录线程和 FD 靠对应的 hook 与计数监控。从“监控”的角度看覆盖面本身就是价值。你不可能只监控 Java 堆然后指望 Native 的问题自己消失。3.6 采样、灰度与上报成本线上监控如果全量开启成本会失控。所以采样是必需品只在部分设备上开启、只在特定版本上开启、只在满足某些条件比如前台、电量充足、非低端机时才触发。LeakCanary 的设计里没有“采样”这个概念因为它压根没考虑多设备场景。KOOM 把采样做成了配置项你可以控制开启比例、单设备每天的最大触发次数、两次触发之间的最小间隔。这几个参数看起来不起眼但它们直接决定了你的服务端能不能扛住。我的经验是采样率不要设成固定值要跟版本和问题收敛情况联动。新版本刚上线、或者刚修完一批泄漏、或者刚接入监控想摸底的时候比例可以调高等报告收敛、问题清单稳定了就往下压只保留一个低比例的常态监控。这套动态调整的机制比一开始就纠结“到底该设 1% 还是 5%”有用得多。4. 落地配置从接入到第一份线上报告理论讲完说说实际怎么落地。这部分是我认为最容易出问题的地方因为大部分坑都不在原理层面而在配置和链路上。4.1 初始化时机与几个绕不开的配置初始化的位置建议放在Application.onCreate里但要注意别跟其他重型初始化抢资源。KOOM 的初始化本身不重主要是注册监听器、读配置真正的开销在触发时刻。配置项大致分几组我这里给的是结构性示意具体字段名以你接入的版本为准// 示意性配置字段以实际接入版本为准 KGlobalConfig.init(application, KConfig.Builder() // 开关组按需开启不要一次全开 .enableJavaLeakMonitor(true) .enableNativeLeakMonitor(false) // 先关跑稳了再开 .enableOomMonitor(true) // 采样组控制打扰面 .setSampleRatio(0.02) .setMaxTriggerPerDay(3) .setMinTriggerInterval(6 * 60 * 60 * 1000L) // 条件组限定触发场景 .setTriggerOnlyInBackground(true) .setMinApiLevel(26) .setOnly64Bit(true) .build())几个必须说清楚的点第一Native 监控不要跟 Java 监控同时开。Native 监控需要在进程启动早期做 hook侵入性更强出问题的概率也更高。先让 Java 这条链路跑通、跑稳、报告能正常解析再考虑往上加。一次性全开的团队最后往往是全线回滚。第二务必限制系统版本下限。低版本 ART 的行为差异大fork 之后子进程死锁的概率明显更高。把下限设在 8.0 甚至更高用少量覆盖率换稳定性这笔账是划算的。真需要覆盖低版本那就单独开一个小比例的实验组去试。第三只开 64 位。前面说过地址空间的限制32 位进程上这套方案基本没有意义强行开启只会增加失败日志。4.2 采样策略别用一个固定比例打天下采样这块我踩过坑。一开始设了个看起来很保守的比例结果上线第一周报告量还是把解析队列打满了因为泄漏的触发是聚集性的——同一处泄漏往往在某个页面的某个操作路径上集中出现一旦有用户走到那条路径就会连续触发。固定的比例在这种突发流量面前完全不够用。后来调成了三层控制设备维度限流单台设备每天最多触发 N 次两次之间至少间隔几小时。这一层挡住的是“同一台设备反复踩同一个坑”。版本维度控制只在灰度版本上开启较高比例全量版本上压到很低。这一层挡住的是“问题还没确认就全量铺开”。全局配额服务端侧做一个每日总量上限超了就自动降级采样率。这一层是最后的安全阀。有了这三层即使某一处泄漏突然爆发你的解析链路也不会被打挂因为端上已经在源头把量掐住了。4.3 混淆与符号还原发版归档是硬性纪律这一条听起来很无聊但它是线上监控最容易翻车的地方。线上包是混淆过的hprof 里记录的是a.b.c这样的类名。如果发版的时候没有把对应的 mapping 文件归档你拿到一份线上报告里面全是一堆单字母等于白拿。而且这个错误是不可逆的——版本发出去之后mapping 就再也找不回来了。我们现在的做法是把 mapping 归档写进发布流水线打包产物一生成mapping 自动上传到内部的文件服务按版本号 渠道 构建号命名跟构建记录绑定。解析服务在处理报告之前先去拉对应版本的 mapping拉不到就直接标记为“无法解析”而不是强行解析出一堆乱码。注意如果你们用的是分渠道、分 ABI 打包每个产物都有自己的 mapping别只归档主包的那一份。除了类名还有一层是 Native 符号。如果你开了 Native 泄漏监控堆栈里会有 so 的地址需要对应的符号表带调试信息的那种才能还原成函数名。这个符号表的体积比 mapping 大得多归档策略要单独设计通常只保留带符号的关键 so其余按需。4.4 一次性把服务端链路打通别边跑边补这是我在第一个项目上犯的错。当时客户端先上线了服务端解析还没做完结果攒了几万份报告堆在那里等解析服务上线的时候发现数据格式已经迭代了两版对不上只能全部作废重来。正确的顺序应该是反过来的先把“报告接收 - 解析 - 归并 - 展示”的链路跑通用一个手工构造的样本数据验证全流程然后再让客户端上线。样本数据的构造也不难本地跑一次 dump把裁剪后的产物当测试用例喂进去看看能不能正确还原出调用链。5. 数据回流之后怎么把几千条报告收敛成几个待修问题监控接上了、数据回来了真正的麻烦才刚开始。不做归并的话你面对的是几千条看起来差不多的报告没人愿意读读了也修不完。5.1 归并的键怎么选归并的核心是选一个稳定的“指纹”。我试过几种方案归并键效果问题泄漏对象的类名太粗一个Activity泄漏会把几十个不同原因混成一条完整引用链哈希太细链路上任何一层变了就是新问题同一原因可能裂成几十条引用链中“第一个业务类 持有者字段名”比较合适需要能识别哪些类是业务类引用链中最后三层业务类 泄漏对象类型比较合适对混淆后的类名依赖 mapping我们现在用的是第三种为主、第四种为辅先按“持有泄漏对象的那个业务类的类名 字段名”做主键因为绝大多数泄漏的本质就是“某个对象的某个字段忘了清理”主键相同但链路不同的再做一次二级聚类作为同一个问题的不同变体展示。这个方案的好处是它能直接对应到代码。你看到主键是某个 Manager 类的某个 List 字段基本就知道该去哪一行加remove了。5.2 看报告时先看这三个数一份归并后的报告我一般先看三个数字影响设备数这个决定了优先级。影响 5000 台的问题和影响 5 台的问题处理顺序完全不同。单设备累计增长同一台设备上这个泄漏重复触发的次数。次数高说明它是高频路径用户的真实体感更差。机型与系统版本分布如果集中在某几个机型上那更可能是 ROM 相关的兼容问题而不是纯业务代码问题排查方向完全不同。这三个数之外的东西比如具体引用链是用来验证判断的不是用来做第一轮筛选的。很多团队一上来就盯着引用链看结果花了两天时间修了一个只影响三台设备的问题。5.3 从报告到修复的闭环修完之后怎么验证这里有个坑你没法保证修完就不漏了只能保证这一条链路不漏了。所以修复之后要做两件事一是在本地用 LeakCanary 复现一遍原来的路径确认那条 Leak Trace 不再出现。这是最直接的验证也是 LeakCanary 在开发期不可替代的地方。二是在线上等版本发布之后看同一个归并键下的报告数是否归零。如果还有残留那说明你的归并键把两个不同原因混在一起了得回头拆细。这个“线上发现 - 本地复现 - 修复 - 线上验证”的循环跑顺了内存泄漏这件事才真正从“靠运气”变成了“可管理”。6. 什么时候我仍然会把 LeakCanary 摆在第一位说了这么多 KOOM 的好话得把话讲完整。LeakCanary 在我们的工程体系里从来没被移除过它只是被限制在了开发期。6.1 开发期定位这件事LeakCanary 目前还是最好的Shark 的分析质量和 Leak Trace 的呈现方式在“我要马上知道这行为什么漏了”这个场景下依然是效率最高的。它的零配置接入、失败即通知、点击跳转到源码的能力构成了一个非常短的反馈闭环。你改一行代码跑一次几秒钟后就知道对不对。KOOM 做不到这一点因为它的价值建立在“服务端”和“采样”之上这两个前提在本地开发时都是负资产。你总不能在本地开个服务端来解析自己的堆。6.2 两者并存的构建配置实际做法很简单用不同的构建变体隔开dependencies { // 只在开发和内部测试包里带上 debugImplementation(com.example.leakcanary:leakcanary-android:2.x) // KOOM 跟着 release 走 releaseImplementation(com.example.koom:koom-java-leak:2.x) }有一类场景需要两个都开灰度包。灰度包本身是 release 变体常规做法是只带 KOOM。但如果你想在灰度阶段同时拿到一份本地可读的详细报告可以给灰度包单独做一个变体把两个都带上并且把 LeakCanary 的触发条件收紧比如只在充电且息屏时触发避免影响灰度用户的体验。6.3 一份可以直接抄的判断清单最后给一个我们内部用的判断表如果你正在纠结对着填一遍基本就有答案了。你的情况建议项目刚起步还没上过线上监控先用 LeakCanary 把开发期的习惯养起来线上 OOM 率高但不知道漏在哪上 KOOM先把 Java 泄漏这条链路跑通只有一两个页面偶发卡顿怀疑内存问题本地用 LeakCanary 复现别急着上线上方案已经有完善的崩溃平台和数据链路KOOM 的接入成本会低很多可以上团队没有服务端资源做解析先别上 KOOM用 LeakCanary 的 release 形态 自建简单解析主要问题是 Native 内存涨KOOM 的 Native 监控是目前少数可选项但要做好长期调优的准备应用体积敏感不能加太多依赖LeakCanary 只在 debug 变体里对线上包体积没影响KOOM 要评估我个人在实际操作中的体会是这类工具的价值从来不在“能不能发现问题”而在“发现问题到修掉问题之间那段路有多短”。LeakCanary 把这段路缩到了几秒钟代价是只能在开发机上跑KOOM 把这段路铺到了线上代价是需要一整套服务端链路和长期的采样调优。两者都不是银弹但放在各自该在的位置上配合起来用才是把内存问题真正管住的样子。如果非要给一个起步建议先把 LeakCanary 用扎实让团队里每个人都对“什么叫引用链”有直觉等到线上 OOM 数据开始影响核心指标的时候再考虑接 KOOM并且第一版一定只开 Java 泄漏、只开 64 位、只开高版本、采样压到最低。跑两周看看报告质量和服务端承压情况再决定要不要扩。急着一次性全开的大概率会在第二周回滚。