【发布时间】:2011-01-29 00:14:32
【问题描述】:
我有一个使用大量数据数组的 C++ 应用程序,并且在测试时注意到它内存不足,但仍有大量可用内存。我已将代码简化为示例测试用例,如下所示;
void MemTest()
{
size_t Size = 500*1024*1024; // 512mb
if (Size > _HEAP_MAXREQ)
TRACE("Invalid Size");
void * mem = malloc(Size);
if (mem == NULL)
TRACE("allocation failed");
}
如果我创建一个新的 MFC 项目,包含这个函数,并从 InitInstance 运行它,它在调试模式下工作正常(按预期分配内存),但在释放模式下失败(malloc 返回 NULL)。单步发布到 C 运行时,我的函数被内联我得到以下
// malloc.c
void * __cdecl _malloc_base (size_t size)
{
void *res = _nh_malloc_base(size, _newmode);
RTCCALLBACK(_RTC_Allocate_hook, (res, size, 0));
return res;
}
调用 _nh_malloc_base
void * __cdecl _nh_malloc_base (size_t size, int nhFlag)
{
void * pvReturn;
// validate size
if (size > _HEAP_MAXREQ)
return NULL;
'
'
And (size > _HEAP_MAXREQ) 返回 true,因此我的内存没有被分配。将手表大小返回到预期的 512MB,这表明该程序正在链接到具有小得多的 _HEAP_MAXREQ 的不同运行时库。 Grepping _HEAP_MAXREQ 的 VC++ 文件夹显示预期的 0xFFFFFFE0,所以我无法弄清楚这里发生了什么。任何人都知道会导致此问题的任何 CRT 更改或版本,还是我遗漏了更明显的东西?
编辑:正如 Andreas 所建议的,在这个装配视图下查看它会显示以下内容;
--- f:\vs70builds\3077\vc\crtbld\crt\src\malloc.c ------------------------------
_heap_alloc:
0040B0E5 push 0Ch
0040B0E7 push 4280B0h
0040B0EC call __SEH_prolog (40CFF8h)
0040B0F1 mov esi,dword ptr [size]
0040B0F4 cmp dword ptr [___active_heap (434660h)],3
0040B0FB jne $L19917+7 (40B12Bh)
0040B0FD cmp esi,dword ptr [___sbh_threshold (43464Ch)]
0040B103 ja $L19917+7 (40B12Bh)
0040B105 push 4
0040B107 call _lock (40DE73h)
0040B10C pop ecx
0040B10D and dword ptr [ebp-4],0
0040B111 push esi
0040B112 call __sbh_alloc_block (40E736h)
0040B117 pop ecx
0040B118 mov dword ptr [pvReturn],eax
0040B11B or dword ptr [ebp-4],0FFFFFFFFh
0040B11F call $L19916 (40B157h)
$L19917:
0040B124 mov eax,dword ptr [pvReturn]
0040B127 test eax,eax
0040B129 jne $L19917+2Ah (40B14Eh)
0040B12B test esi,esi
0040B12D jne $L19917+0Ch (40B130h)
0040B12F inc esi
0040B130 cmp dword ptr [___active_heap (434660h)],1
0040B137 je $L19917+1Bh (40B13Fh)
0040B139 add esi,0Fh
0040B13C and esi,0FFFFFFF0h
0040B13F push esi
0040B140 push 0
0040B142 push dword ptr [__crtheap (43465Ch)]
0040B148 call dword ptr [__imp__HeapAlloc@12 (425144h)]
0040B14E call __SEH_epilog (40D033h)
0040B153 ret
$L19914:
0040B154 mov esi,dword ptr [ebp+8]
$L19916:
0040B157 push 4
0040B159 call _unlock (40DDBEh)
0040B15E pop ecx
$L19929:
0040B15F ret
_nh_malloc:
0040B160 cmp dword ptr [esp+4],0FFFFFFE0h
0040B165 ja _nh_malloc+29h (40B189h)
寄存器如下;
EAX = 009C8AF0 EBX = FFFFFFFF ECX = 009C8A88 EDX = 00747365 ESI = 00430F80 EDI = 00430F80 EIP = 0040B160 ESP = 0013FDF4 EBP = 0013FFC0 EFL = 00000206
所以比较似乎与正确的常数相反,即@040B160 cmp dword ptr [esp+4],0FFFFFFE0h,还有 esp+4 = 0013FDF8 = 1F400000(我的 512mb)
第二次编辑:根据 Andreas 的帖子,问题实际上出在 HeapAlloc 中。使用 HeapCreate 和 HeapAlloc 更改为大型对象的新单独堆并没有帮助缓解问题,也没有尝试将 VirtualAlloc 与各种参数一起使用。一些进一步的实验表明,在分配一大块连续内存失败的情况下,两个较小的块产生相同的总内存是可以的。例如如果 300MB malloc 失败,则 2 x 150MB malloc 可以正常工作。所以看起来我需要一个新的数组类,它可以存在于许多大内存片段中,而不是单个连续块中。不是什么大问题,但在这个时代,我对 Win32 的期望会更高一些。
上次编辑:以下产生了 1.875GB 的空间,尽管不连续
#define TenMB 1024*1024*10
void SmallerAllocs()
{
size_t Total = 0;
LPVOID p[200];
for (int i = 0; i < 200; i++)
{
p[i] = malloc(TenMB);
if (p[i])
Total += TenMB; else
break;
}
CString Msg;
Msg.Format("Allocated %0.3lfGB",Total/(1024.0*1024.0*1024.0));
AfxMessageBox(Msg,MB_OK);
}
【问题讨论】:
-
如果你看反汇编,
_HEAP_MAXREQ在>-comparison中使用了什么值?
标签: c++ visual-c++ malloc