我正在尝试在Windows应用程序中使用realloc() 。 我分配一大块内存,然后一旦我知道正确的大小,然后使用realloc()将其缩小。
我发现尽piperealloc()似乎正常工作(任务pipe理器中的内存反映了你所期望的)应用程序最终会耗尽内存。 从我可以告诉,就好像relloc()释放内存,但不释放与内存相关的虚拟地址空间。 因此, malloc()最终会失败。
这是一个小的控制台应用程序,演示了这个问题:
int _tmain(int argc, _TCHAR* argv[]) { static const DWORD dwAllocSize = (50 * 1024 * 1024); static const DWORD dwReallocSize = 10240; static const DWORD dwMaxIterations = 200; BYTE* arpMemory[dwMaxIterations]; memset( arpMemory, 0, sizeof(arpMemory) ); for( DWORD i = 0; i < dwMaxIterations; i++ ) { arpMemory[i] = (BYTE*) malloc( dwAllocSize ); if( !arpMemory[i] ) { printf("OUT OF MEMORY after %d iterations!\n", i); return -1; } BYTE* pRealloc = (BYTE*) realloc( arpMemory[i], dwReallocSize ); if( !pRealloc ) { printf("Realloc FAILED after %d iterations!\n", i); return -1; } else if( pRealloc != arpMemory[i] ) { printf("Warning: Pointer changed: 0x%08X -> 0x%08X\n", arpMemory[i], pRealloc); arpMemory[i] = pRealloc; } } printf("Success!\n"); for( int i = 0; i < dwMaxIterations; i++ ) free( arpMemory[i] ); return 0; }
该应用程序重复分配50 MB的内存,然后立即调整它只有10K。 如果你运行它,你会发现它只有38次迭代后失败,并出现OUT OF MEMORY错误。 这对应于2GB原始分配的内存 – 这是Windows应用程序的地址空间限制。
有趣的是,如果你看任务pipe理器,你会看到应用程序几乎没有记忆。 然而malloc()失败。 这使我相信虚拟地址空间正在耗尽。
(另一个尝试是注销重新分配,所以没有内存被释放或重新分配,应用程序在完全相同的地方失败:经过38次迭代,唯一的区别是这次任务pipe理器反映了2GB的使用。
最后一点信息:这个相同的应用程序在Linux下工作。 所以这个realloc()问题是严格的Windows只。
有什么想法吗?
你正在分割堆这样做。 无论你用realloc()发布什么,都会被添加到空闲块的列表中。 永远不要再被使用,因为你总是要求一个比这更大的新块。 这些空闲块将累积起来,占用虚拟内存,直到没有剩余空间。 当你每次扔掉差不多50兆字节时,速度会非常快。
你需要重新思考你的方法。
经过一些实验和读取文档之间的内容,我得出的结论是,使用VirtualAlloc分配的地址空间使用大内存分配(对于32位略低于512k,对于64位稍低于1MB),并保留对于那个特定的内存块。
如果按照Hans的说法增加缓冲区大小是不实际的,而且你不想复制数据,那么我认为你唯一的选择是为所有的缓冲区保留一个足够大的地址空间块并且分配它自己的空间。 这会有多复杂取决于诸如应用程序如何使用和释放缓冲区,是否一次写入多个缓冲区,以及在应用程序的整个生命周期中将处理多少数据等因素。
另外:这里是一些测试代码,显示我看到的情况:
#include <windows.h> #include <stdio.h> DWORD reallocSize = 0x01000; // 4K void walkheap(HANDLE heap) { PROCESS_HEAP_ENTRY phe; MEMORY_BASIC_INFORMATION mbi; phe.lpData = NULL; for (;;) { if (!HeapWalk(heap, &phe)) { printf("HeapWalk: %u\n", GetLastError()); return; } printf("%08x %08x %08x %08x %08x ", phe.lpData, phe.cbData, phe.cbOverhead, phe.iRegionIndex, phe.wFlags); if (VirtualQuery(phe.lpData, &mbi, sizeof(mbi)) != 0) { printf("--> %08x\n",mbi.AllocationBase); } else { printf("--> (error %u)\n", GetLastError()); } } } void alloc(HANDLE heap, DWORD count, DWORD size) { BYTE* ptr; BYTE* pRealloc; ptr = (BYTE *)HeapAlloc(heap, 0, size); printf("Pointer %u is %08x (%08x)\n", count, ptr, size); pRealloc = (BYTE*) HeapReAlloc( heap, 0, ptr, reallocSize); if( pRealloc != ptr) { printf("Pointer %u changed to %08x\n", count, pRealloc); } } int main(int argc, char ** argv) { HANDLE heap; heap = HeapCreate(0, 0, 0); if (heap == NULL) { printf("HeapCreate: %u\n", GetLastError()); return 1; } walkheap(heap); alloc(heap, 1, 0x08000); alloc(heap, 2, 0x08000); alloc(heap, 3, 0x08000); alloc(heap, 4, 0x08000); alloc(heap, 10, 0x20000000); alloc(heap, 11, 0x20000000); alloc(heap, 12, 0x20000000); alloc(heap, 13, 0x20000000); alloc(heap, 20, 0x10000000); alloc(heap, 21, 0x10000000); alloc(heap, 22, 0x10000000); alloc(heap, 23, 0x10000000); walkheap(heap); return 0; }
和我的结果(请参阅PROCESS_HEAP_ENTRY结构):
Address Alloc Overhead Region Flags Virtual Address Range Base 00420000 00000588 00000000 00000000 00000001 --> 00420000 004207e8 000007f8 00000010 00000000 00000000 --> 00420000 00421000 0003f000 00000000 00000000 00000002 --> 00420000 HeapWalk: 259 Pointer 1 is 004207e0 (00008000) Pointer 2 is 004217f8 (00008000) Pointer 3 is 00422810 (00008000) Pointer 4 is 00423828 (00008000) Pointer 10 is 00740020 (20000000) Pointer 11 is 20750020 (20000000) Pointer 12 is 52580020 (20000000) Pointer 13 is 00000000 (20000000) Pointer 20 is 40760020 (10000000) Pointer 21 is 00000000 (10000000) Pointer 22 is 00000000 (10000000) Pointer 23 is 00000000 (10000000) 00420000 00000588 00000000 00000000 00000001 --> 00420000 004207e0 00001000 00000018 00000000 00000004 --> 00420000 004217f8 00001000 00000018 00000000 00000004 --> 00420000 00422810 00001000 00000018 00000000 00000004 --> 00420000 00423828 00001000 00000018 00000000 00000004 --> 00420000 00424848 0000e798 00000010 00000000 00000000 --> 00420000 00433000 0002d000 00000000 00000000 00000002 --> 00420000 00740020 00001000 00000020 00000040 00000024 --> 00740000 20750020 00001000 00000020 00000040 00000024 --> 20750000 52580020 00001000 00000020 00000040 00000024 --> 52580000 40760020 00001000 00000020 00000040 00000024 --> 40760000 HeapWalk: 259
可以看出,小的分配是紧紧包装的,但是大的分配都是分开的虚拟地址分配。 没有使用单独分配中的可用空间。 此外,只有主虚拟地址分配有任何被标记为空闲空间(标志等于0或2)的堆块。