所以我有一个本机 C++ 应用程序,它需要长时间跟踪很多事情。当任务管理器说进程达到 800 到 1200 MB 内存之间的某个位置时,它的内存不足,而限制应该是大约 2GB。
当我对我的进程运行 VMMap 时,我终于知道发生了什么,但这只是给了我更多的问题。我发现了什么:
- 总大小(类型:总计,列:大小)比任务管理器/进程资源管理器报告的要大得多
- 在我的程序内存不足之前,总大小似乎实际上是不能超过 2GB 的值
- 内存使用差异几乎完全是由“私人数据”引起的——“大小”比“承诺”多得多。我见过大约 800MB 已提交的私有数据,但“大小”约为 1700MB。
- “私有数据”的最大块主要由一对具有“读/写”保护并完全提交的小子块(通常在 4K 和 16K 之间)和一个较大的子块(介于90K 和 400K)具有“保留”保护且未提交。这似乎是对资源的巨大浪费。并且通常在末尾有一个大的(数兆字节)子块是“保留的”并且未提交。
- 这对中的一小部分通常有我认识的字符串,而较大的部分根本没有字符串。
这些子块对的一个例子:(不是我的应用程序,但想法是一样的) http://www.flickr.com/photos/95123032@N00/5280550393
似乎当一个私有数据块被完全提交时,一个新块(通常与前一个最大块的大小相同或两倍)被分配。听起来很公平。但是,我看到了 3 个块,每个块都超过 100MB,提交的块不到 30MB。我的应用程序不应该以这种可能的方式运行(即用完 400MB,然后在几个小时内缩小 300MB)。
据我所知,“大小”是已分配的虚拟内存地址空间的实际数量。“已提交”是实际使用的“大小”数量(即通过调用 new/malloc)。如果确实如此,那为什么 Size 和 Commited 之间会有如此巨大的差异呢?为什么要分配数百兆字节的块?
有点奇怪的是,在 Windows 7 上运行时的行为完全不同。而在 2003 Server 上,应用程序使用私有数据,而在 Windows 7 上,应用程序使用堆。所以为什么?为什么 VMMap 在 2003 上主要显示私有数据使用情况,而在 7 上主要显示堆使用情况?有什么不同?一个区别是我不能使用 VMMap 中的“堆分配...”按钮来查看所有私有数据的分配位置。
我开始怀疑是否过度使用 std::string 导致了这个问题,因为我在成对中识别的字符串(如上所述)主要由存储在 std::string 中的字符串组成,这些字符串经常被创建和销毁(暗示很多内存分配/释放)。我将所有可能的转换为使用字符数组或使用内存池中的内存,但这似乎没有效果。我所有其他经常新建/删除的对象都已经拥有自己的内存池。
我还发现了低碎片堆,所以我尝试启用它,但它也没有任何区别。我认为这是因为 Windows 2003 实际上并没有正确使用堆。VMMap 显示启用了低碎片堆,但由于它实际上并没有被使用(即它使用的是私有数据),它实际上并没有什么不同。
实际上似乎正在发生的是,这些子块对正在分割大型私有数据块,这导致操作系统分配新块。最终,碎片变得如此糟糕,以至于即使有很多未提交的空间,但似乎没有一个可用,并且进程内存不足。
所以我的问题是:
- 为什么 Windows Server 2003 使用私有数据而不是堆?有关系吗?有没有办法让 Windows Server 2003 改用堆内存?如果是这样,那会改善我的情况吗?
- 有什么方法可以控制操作系统的内存分配器如何分配私有数据?
- 是否可以创建我自己的自定义堆并从中分配(不改变我的大部分代码库),这能改善我的情况吗?我知道可以制作自定义堆,但据我所知,您需要从自定义堆中显式分配,而不是仅仅调用 new 或仅正常使用 STL 容器。
- 我有什么遗漏或值得尝试的吗?