PCL2内存优化原理
分析了
MemSwapWorks类的实现,找到了内存优化的原理。

一、内存优化生效的核心原因
具体代码
public static bool MemorySwap(bool showHint = true)
{
if (!_MemSwapLock.Wait(0))
{
Context.Warn("检测到正在进行的内存处理,取消当前处理");
if (showHint) HintWrapper.Show("内存优化尚未结束,请稍等!");
return false;
}
Context.Info("收到内存交换请求,开始处理");
if (showHint) HintWrapper.Show("正在进行内存优化");
try
{
var before = KernelInterop.GetAvailablePhysicalMemoryBytes();
Context.Info($"处理前内存量 {ByteStream.GetReadableLength((long)before)}");
// 添加内存处理提权操作
PromoteService.Append("mem-swap", result =>
{
try
{
if (result is null)
{
var after = KernelInterop.GetAvailablePhysicalMemoryBytes();
Context.Info($"处理后内存量 {ByteStream.GetReadableLength((long)after)}");
var diff = Math.Max(0, after - before);
var afterStr = ByteStream.GetReadableLength((long)after);
var diffStr = ByteStream.GetReadableLength((long)diff);
Context.Info($"处理结束,总共处理 {diffStr}");
if (showHint) MsgBoxWrapper.Show($"内存优化结束,共优化 {diffStr},目前可用内存 {afterStr}");
}
else
{
Context.Error($"内存优化失败\n\n详细信息: {result}", actionLevel: ActionLevel.MsgBoxErr);
}
}
catch (Exception ex) { Context.Error("内存优化失败", ex); }
finally { _MemSwapLock.Release(); }
});
// 执行操作
if (PromoteService.Activate()) return true;
_MemSwapLock.Release();
if (showHint) MsgBoxWrapper.Show("提权进程启动失败,请允许管理员权限以使用内存优化", "内存优化失败");
return false;
}
catch (Exception)
{
_MemSwapLock.Release();
throw;
}
}先纠正一个常见误解:这段代码并没有去"关闭"或"压缩" Minecraft / Java 真正在用的内存,它做的事情更像是**"提权后,让 Windows 内核重新整理一遍系统物理内存账本"**,把那些"看起来被占用、其实可以释放"的部分让出来,从而让任务管理器/资源监视器里的"已用内存"数字明显下降。
代码里通过 PromoteService 拿到管理员权限后,先开启两个关键特权(SeProfileSingleProcessPrivilege、SeIncreaseQuotaPrivilege),然后按 MemSwapScope 标志位依次调用 NT 内核未公开/半公开的 NtSetSystemInformation 接口,执行 7 项操作。用大白话翻译就是:
| 代码里的操作 | 通俗解释 | 对"占用 16G"的贡献 |
|---|---|---|
EmptyWorkingSets (值 2) | 把所有进程当前的工作集(Working Set,可以理解为"常驻在物理内存里的活跃页面")强制踢到分页文件/备用列表里 | 这是降数字的"主力"。MC + Java 堆原本压在物理内存里几个 G,被踢出去后,物理占用瞬间掉下来 |
FlushFileCache | 把系统文件缓存的最大/最小工作集都设成 uint.MaxValue 这种特殊值,触发文件缓存收缩 | MC 启动时读了大量 jar/资源包,文件缓存可能吃掉 1~3G,这一步清掉 |
FlushModifiedList (值 3) | 把"已修改但尚未写回磁盘"的内存页强制写盘并转入备用列表 | 让"脏页"尽快变成可释放页 |
PurgeStandbyList (值 4) | 清空"备用列表"——Windows 用来加速二次访问的缓存层 | 这是数字下降的另一大头,能瞬间空出几个 G |
PurgeLowPriorityStandbyList (值 5) | 只清低优先级备用列表,比上面温和 | 同上但范围小一些 |
RegistryReconciliation | 让注册表 hive 在内存中的副本做一次回收整理 | 小头,几十 MB 级别 |
CombinePhysicalMemory | 调用 Win8.1+ 的"内存合并"功能,把内容相同的物理页合并成一份 | 中等收益,对开多实例/多 Java 进程时效果更明显 |
为什么"16G → 8G"看起来这么神奇:因为这 7 步合起来,把"工作集 + 文件缓存 + 备用列表 + 修改列表 + 重复页"四五个口袋同时往外掏。其中 EmptyWorkingSets 和 PurgeStandbyList 是降数字最猛的两手——它们让任务管理器里"已用内存"立刻塌一截。
但要注意:被踢出去的页面并没有消失,它们去了三个地方——① 分页文件(pagefile.sys,硬盘上)、② 备用列表(仍在物理内存,标记为"可回收")、③ 真正被释放。所以"省下来的 8G"里,相当一部分其实是从物理内存挪到了硬盘,并不是无中生有。
二、长期使用适用性判断
结论:适合"按需手动触发",不适合"挂着定时跑 / 一直开着"。
适合的场景:
- MC 关掉之后点一下,把残留在备用列表里的几个 G 资源包缓存清出来,给下一个程序腾地方 —— 这是最划算的用法。
- 物理内存吃紧、即将爆掉时救急一次,比直接卡死或触发系统级换页风暴要温和。
- 偶尔在开 MC 前清一次,让 JVM 拿到一块更"干净"的连续物理内存。
不适合长期/高频使用的原因:
- 它对抗的是 Windows 内存管理器的设计哲学。Windows 的理念是"空闲内存就是浪费的内存",备用列表和文件缓存本来就是为了加速;你频繁清空,等于一直在拆系统给你搭好的缓存。
- MC 运行中调用,会立刻引发性能抖动。工作集被强制清空后,JVM/MC 下一次访问那些页面时要从 pagefile 重新读回,直接表现为卡顿、掉帧、甚至几秒的假死。
- "优化效果"会随着程序继续运行迅速回血。你刚清完看到掉了 6G,过几分钟 MC 再加载区块、贴图,又涨回去了,治标不治本。
- 真正的内存瓶颈是 JVM 堆大小
-Xmx,那部分根本不会被这段代码动到(堆是 JVM 主动 commit 的活跃内存,踢出去也会被立刻读回)。所以靠它"解决 MC 吃内存"是错位的——根因得改启动参数、装 Sodium/Lithium 等 mod、或者加内存条。
三、潜在使用风险
按真实使用场景从高到低列:
MC/Java 卡顿、掉帧、瞬时无响应(最常见)
- 在游戏运行中点"内存优化",
EmptyWorkingSets会把 MC 的活跃页面也一起踢走,紧接着区块更新、玩家移动会触发大量缺页中断,从硬盘把页面读回来,体感就是"突然一卡几秒"。机械硬盘上尤其明显。
- 在游戏运行中点"内存优化",
SSD 写入量增加,寿命有损耗
FlushModifiedList会把脏页强制写回磁盘,EmptyWorkingSets间接增加 pagefile 读写。每天点几十次这种用法对消费级 SSD 不友好,长期会贡献额外 TBW。
整机短时间内"假性卡顿"
- 因为是对全系统所有进程生效,不只是 MC。点一下后浏览器、IDE、QQ 等都可能跟着卡 1~2 秒,因为它们的工作集也被清了。
CombinePhysicalMemory在极少数情况下兼容性差- 这是 Win8.1 之后的功能,老旧驱动、虚拟化环境、部分服务器系统下调用可能返回失败甚至触发蓝屏(历史上有过相关案例)。代码里只是 try/catch 包了一层,不能完全规避。
依赖未公开 NT API,未来 Windows 大版本升级可能失效
NtSetSystemInformation的这几个 InformationClass(如SystemMemoryListInformation、SystemCombinePhysicalMemoryInformation)是半公开的,微软可以随时改行为甚至禁用。出问题时也无官方文档可查。
提权风险
- 每次都要 UAC 提权拿
SeProfileSingleProcessPrivilege/SeIncreaseQuotaPrivilege。这本身不是恶意,但意味着启动器进程在那一刻有较高权限,万一被恶意 mod/插件利用提权链是个攻击面(PCL-CE 本身是开源可信的,主要风险来自第三方扩展场景)。
- 每次都要 UAC 提权拿
掩盖真实问题
- 数字一掉,用户会以为"内存够用",继续把
-Xmx设得过大、装更多重型 mod,最后还是要面对真·物理内存不够的现实,可能更晚才意识到该加内存条。
- 数字一掉,用户会以为"内存够用",继续把
多次连续点击会被锁挡住
- 代码里用
SemaphoreSlim(1,1)+Wait(0)做了串行保护,重复点击会提示"内存优化尚未结束",这点本身是好的——不会真的并发触发,但用户可能误以为"按了没反应"而反复点。
- 代码里用
一句话总结:这段代码本质是个**"提权后调用 Windows 未公开内核接口清缓存 + 踢工作集 + 合并重复页"的合集**,降数字非常猛但相当一部分是"挪到硬盘"而非"凭空省出",适合 MC 关闭后偶尔救急清一次,不建议在游戏运行中频繁触发,更不建议挂定时任务长期跑。真要根治"MC 吃 16G",还是得回到 -Xmx、mod 选型和加内存条这三件事上。