Windows内存类型介绍
Windows上的内存有好几种说法,比如工作集(Working Set)、提交大小(Private Bytes)、虚拟大小(Virtual Size)。究竟这几种说法有什么区别?每种内存到底指的是什么?哪种内存才能真正反映程序使用的内存情况?虽然做Windows开发这么久,一直对这些只有模糊的认识。如果你去网上找寻答案,可能会找到一些错误的或者矛盾的说法,这更加让人感到迷惑。最近要处理Chromium内核一些因此内存不足而导致的崩溃问题,所以才深入去研究一下这个问题。
Windows上的程序都有自己专有的虚拟地址空间,比如32位的程序地址空间大小是4GB,这是因为32位指针可以表示从0x00000000到0xFFFFFFFF之间的任一值。每个进程的地址空间是专有的,当前进程只能访问到该进程的内存,看不到其他进程的内存。不同进程虽然访问同一个内存地址,实际上它们得到的数据是不同的。
虚拟地址空间不是真正的内存,不能够直接读写。当程序需要读写的时候,操作系统会在背后把物理存储器分配或者映射到相应的地址空间,否则将会导致访问违例(access violation)。
我们可以通过VirtualAlloc API分配一块地址空间的区域,这个操作被称之为预订(reserving)。当程序预订地址空间区域时,操作系统会确保区域的起始地址正好是分配粒度的整数倍。分配粒度会根据不同的CPU平台而有所不同。目前Windows分配粒度是64KB大小。页面是一个内存单元,系统通过它来管理内存。与分配粒度类似,页面大小会根据不同CPU而有所不同,x86和x64页面大小是4KB,而IA-64系统页面大小是8KB。
为了使用预订的地址空间区域,我们还必须分配物理存储器,并将存储器映射到所预订的的区域,这个过程被称之为调拨(committing)物理存储器。物理存储器始终都以页面为单位来调拨。
当我们调拨物理存储器给区域时,并不需要给整个区域都调拨物理存储器。当程序不再需要访问所预订的区域中已调拨的物理存储器时,应该释放物理存储器。这个过程被称之为撤销调拨(decommitting)物理存储器。
当今操作系统能让磁盘空间看起来像内存一样。磁盘上的文件被称之为页交换文件(paging file)。页交换文件以一种透明的方式增大了程序可用的内存(或存储器)的总量。如果一台电脑装备了1GB的内存条,硬盘上还有1GB的页交换文件,那么应用程序会认为可用的内存总量为2GB。
我写了一个inspect_memory的程序来验证各种分配内存方式对不同内存指标的影响:
通过new分配内存10MB,工作集(Working Set)增加10MB,提交大小(Private Bytes)增加10MB,虚拟大小(Virtual Size)增加10MB。
通过VirtualAlloc预订(reserving)内存10MB,工作集(Working Set)大小不变,提交大小(Private Bytes)大小不变,虚拟大小(Virtual Size)增加10MB。
通过VirtualAlloc预订(reserving)并调拨(committing)内存10MB,工作集(Working Set)增加10MB,提交大小(Private Bytes)增加10MB,虚拟大小(Virtual Size)增加10MB。
运行多个inspect_memory实例。在每个实例上通过new分配了很多内存,比如工作集(Working Set)、(Private Bytes)、虚拟大小(Virtual Size)都接近2GB停止。任务管理性能tab上显示的可用的物理内存很少时,发现先前实例的工作集(Working Set)在慢慢减少,而(Private Bytes)、虚拟大小(Virtual Size)保持不变。
通过上述实验,参考https://stackoverflow.com/questions/1984186/what-is-private-bytes-virtual-bytes-working-set可以得出结论:
提交大小(Private Bytes)是进程分配的内存大小。这些内存可能存在于RAM中,也可以存在于页交换文件(paging file)中。
工作集(Working Set)是进程存在于RAM中内存的大小,即不引起page fault异常就能够访问的内存。它通常包括一些可与其他进程共享的内存,比如内存映射文件。
虚拟大小(Virtual Size)是进程地址空间预订的区域大小。
操作系统会自动调度程序工作集(Working Set)内存的使用。
因此我觉得用提交大小(Private Bytes)来衡量程序使用内存的情况比较恰当,虽然提交大小(Private Bytes)不一定真正的占用RAM内存,但是提交大小(Private Bytes)一直增加,那么程序很可能存在内存使用不当的情况。
除了操作系统去调度程序工作集(Working Set)内存的大小,我们也可以通过SetProcessWorkingSetSize,、SetProcessWorkingSetSizeEx、EmptyWorkingSet等API去手动设置工作集(Working Set)的大小。360安全卫士的内存优化功能大概就是通过这些API去实现的。使用这些API减少工作集Working Set)的大小有点像掩耳盗铃,它是把进程存在于RAM中的内存交换到页交换文件(paging file)里,表面上是减少了物理内存的使用,实际上程序真正需要访问这些数据的时候,又需要操作系统把页交换文件(paging file)里数据交换到RAM内存中,增加了不必要的调度,反而影响了程序的运行性能,我们未必有操作系统的工作集(Working Set)调度做的好。
这些内存数据我们可以通过系统提供的API获得:
GetProcessMemoryInfo获得PROCESS_MEMORY_COUNTERS_EX数据,WorkingSetSize字段表示进程的工作集(Working Set)大小。
GetProcessMemoryInfo获得PROCESS_MEMORY_COUNTERS_EX数据,PrivateUsage字段表示进程的提交大小(Private Bytes)大小。
GlobalMemoryStatusEx获得MEMORYSTATUSEX数据,ullTotalVirtual减去ullAvailVirtual就是虚拟大小(Virtual Size)大小。
更多的数据信息可以查阅相关的MSDN文档。
参考:
https://bbs.pediy.com/thread-156036.htm
https://stackoverflow.com/questions/1984186/what-is-private-bytes-virtual-bytes-working-set
https://docs.microsoft.com/zh-cn/windows/desktop/Memory/working-set