如何获取进程可共享与共享工作集大小并优化低效代码?
优化方案与替代API建议
一、现有代码的针对性优化
1. 缓存系统页面大小
系统页面大小在运行时不会改变,无需每次调用都重新获取,直接缓存到静态字段:
private static readonly uint _cachedPageSize = GetMemoryPageSize();
2. 预估初始缓冲区大小,减少扩容次数
原代码初始缓冲区仅16字节,会导致多次调用QueryWorkingSet和内存重分配,开销极大。可以先通过GetProcessMemoryInfo获取进程工作集总大小,估算所需条目数:
// 先获取进程工作集大小,预估条目数 PROCESS_MEMORY_COUNTERS_EX pmc = new(); pmc.cb = (uint)Marshal.SizeOf(typeof(PROCESS_MEMORY_COUNTERS_EX)); if (GetProcessMemoryInfo(processHandle, ref pmc, pmc.cb)) { uint estimatedEntries = (uint)(pmc.WorkingSetSize / _cachedPageSize); size = (uint)(sizeof(ulong) + estimatedEntries * sizeof(nuint)); } else { // 预估失败时使用较大默认值,比如1024条目的大小 size = (uint)(sizeof(ulong) + 1024 * sizeof(nuint)); }
这样大概率能一次性分配足够的缓冲区,避免多次扩容和系统调用。
3. 直接统计目标大小,省略不必要的数组分配
如果你的最终需求只是获取共享/可共享工作集大小,不需要保留所有工作集条目,可直接在遍历内存时统计,无需创建nuint[]数组:
ulong* dataPointer = (ulong*)pointer; uint numberOfEntries = (uint)*dataPointer; dataPointer += 1; ulong sharedPageCount = 0; for (var i = 0; i < numberOfEntries; i++) { // 解析PSAPI_WORKING_SET_BLOCK的Shared标志位(64位系统下为第57位) nuint block = *dataPointer; bool isShared = (block & (nuint)0x2000000000000000) != 0; if (isShared) { sharedPageCount++; } dataPointer += 1; } // 计算共享工作集大小 ulong sharedWorkingSetSize = sharedPageCount * _cachedPageSize; // 可共享工作集需根据Win32Protection标志判断,比如PAGE_READWRITE等可共享属性,按需补充逻辑
省略数组分配和复制操作,能大幅降低CPU和内存开销。
4. 批量内存复制替代逐元素循环(若需保留条目)
如果必须保留所有工作集条目,使用Buffer.MemoryCopy替代手动循环赋值,效率更高:
info.WorkingSetInfo = new nuint[numberOfEntries]; fixed (nuint* dest = info.WorkingSetInfo) { Buffer.MemoryCopy(dataPointer, dest, numberOfEntries * sizeof(nuint), numberOfEntries * sizeof(nuint)); }
二、替代API方案
1. 使用QueryWorkingSetEx
该函数返回更详细的工作集页面信息(PSAPI_WORKING_SET_EX_INFORMATION),直接包含页面共享状态、保护属性等字段,无需手动解析位标志,代码更清晰,效率与QueryWorkingSet相当:
[DllImport("psapi.dll", SetLastError = true)] private static extern bool QueryWorkingSetEx(IntPtr hProcess, void* pv, uint cb); // 使用示例 uint size = ...; // 预估大小 void* pointer = Alloc(size); bool result = QueryWorkingSetEx(processHandle.DangerousGetHandle(), pointer, size); // 后续直接解析PSAPI_WORKING_SET_EX_INFORMATION结构的Shared字段
2. 底层原生API:NtQueryVirtualMemory
若追求极致性能,可调用原生NtQueryVirtualMemory函数(需注意系统兼容性),直接遍历进程虚拟内存区域,判断是否在工作集内、是否为共享页面。不过该API属于未公开接口,需要处理更多底层细节,仅推荐对Windows内核机制熟悉的场景使用。
三、额外优化建议
- 降低调用频率:如果该函数被高频调用(如每秒多次),会累积CPU开销,可增加调用间隔或仅在必要时触发。
- 后台线程执行:将该操作放到后台线程,避免阻塞UI线程,同时分散CPU负载。
内容的提问来源于stack exchange,提问作者dt_overflow
相关产品推荐
相关产品推荐

