1

We have a DLL (built using VC2005) that does some processing on behalf of the calling application. This processing requires quite a bit of memory. The DLL creates this memory via heapAlloc like so:

//Allocate space
myStruct* pStackSpace = (myStruct*)::HeapAlloc(m_hStackHeap, 0, sizeof(myStruct));

...
do some processing here
...

//Free space
::HeapFree(m_hStackHeap, 0, pStackSpace);

The heap is allocated via:

m_hStackHeap = ::HeapCreate(0, sizeof(myStruct)*10, 0);

After the create we actually allocate 20 myStructs and then free them just to make sure it handle that. So we know there is enough space.

The issues is that in some case HeapAlloc returns NULL. If that happens we do a HeapValidate(m_hStackHeap, 0, NULL) which always comes back nonezero (meaning all is well). So we know the heap is ok.

We also grantee that we never have more then 10 concurrent allocation at the same time so there should be enough space since the initial heapCreate reserved that much.

The next call to HeapAlloc often succeeds. The behavior is very sporadic. It will work fine, then fail to allocate a few times and then start working fine again.

Any ideas on what is going on?

Chad
  • 18,706
  • 4
  • 46
  • 63
Sam
  • 11
  • 1
  • 3
  • My assumption would be that there must be a problem in tracking concurrent allocations. How are you tracking to ensure you only have 10? – Chad Aug 05 '11 at 18:30
  • 2
    Have you passed HEAP_GENERATE_EXCEPTIONS as an option? It will tell you which of the two failure modes (out of memory vs. corrupted heap) it hit. – Ernest Friedman-Hill Aug 05 '11 at 18:33
  • since you are using DLLs, are you accessing heap, created by one process from another? *The memory of a private heap object is accessible only to the process that created it.* -- from MSDN – Gene Bushuyev Aug 05 '11 at 18:41
  • How do you create the heap? If you're using a limited size heap (e.g. non-zero `dwMaximumSize` on creation), each allocation is limited to something like 512K, even if the heap is very large. What size does `sizeof(myStruct)*10` give you? – André Caron Aug 05 '11 at 19:06
  • Chad -- It is a bit more complex. This is a DLL that gets called from Java. The concurrency is implemented on the Java end where it will not call into the DLL if it already has 9 peddling calls. We have a ton of log messages to verify this. Also the HeapCreate is set with zero max size so we would have to have way more 2x to cause it to run out of memory. Of course you could be right and we could have concurrency issue but I think it is very unlikely. – Sam Aug 05 '11 at 19:11
  • Ernest no I am did not try the exception, but since we run HeapValidate right after the failure and it comes back ok, would it not be safe to assume it is a memory issue? – Sam Aug 05 '11 at 19:13
  • 1
    Andre it is not limited: ::HeapCreate(0, sizeof(myStruct)*10, 0) we are passing zero for the max. – Sam Aug 05 '11 at 19:14

3 Answers3

1

The behavior suggests it could be due to heap fragmentation. You may have sufficient total heap space to satisfy the request but no free blocks large enough. Try using a Low Fragmentation Heap. You can do that by calling HeapSetInformation() to enable LFH. Note that you can't use LFH if you specified the HEAP_NO_SERIALIZE flag in HeapCreate().

Carey Gregory
  • 6,836
  • 2
  • 26
  • 47
  • I am always allocating the same size so I would thing fragmentation would not be an issue? – Sam Aug 05 '11 at 20:29
  • Hmm... he uses dwMaximumSize==0 in `HeapCreate`, meaning the heap can grow, so even if fragmentation were serious issue the heap should be able to keep "outgrowing" it for a long time. – Branko Dimitrijevic Aug 05 '11 at 20:57
  • @Sam: It wasn't clear you always allocate the same size. If that's true, then you're right that fragmentation shouldn't be an issue. However, it's so easy to try I would just to be sure. What would also be helpful here would be some numbers. How large is this struct? – Carey Gregory Aug 05 '11 at 21:24
  • @Branko: A growable heap != an infinite heap. – Carey Gregory Aug 05 '11 at 21:25
  • @cary good point no harm in trying. I will do that. As for sizes the structure we allocate is about 2MB. I also do a sanity check right after I create the heap like so: ` int size = 20; myStruct** pFullStackData= new myStruct*[size]; for (int i=0; i – Sam Aug 05 '11 at 22:42
  • I also do a heapwalk after each failure so I can share that too if it is helpful. – Sam Aug 05 '11 at 22:42
  • 2 MB x 20 shouldn't be a problem, so you're not truly exhausting memory or even close to it. Something else is going on here. Does HeapAlloc in your sanity check ever fail? – Carey Gregory Aug 05 '11 at 23:00
  • @Carey no it never fails. HeapValidate does not fail either. It is rather strange. – Sam Aug 06 '11 at 00:17
0

Rather than use a custom heap you can use custom ALLOC and FREE routines that keep a pool of the appropriate size.

This is done with unioning the struct with a simple object containing a NEXT pointer and one global variable containing a pointer.

If you're out allocate a new one from the global heap.

Where you would have destroyed the heap free all these.

Joshua
  • 40,822
  • 8
  • 72
  • 132
0

2mb structs? Consider using VirtualAlloc and a alloc/free pointer list.

Joshua
  • 40,822
  • 8
  • 72
  • 132
  • Unless I am mistaken HeapAlloc will map to VirtualAlloc especially if the Heap is created with dwMaximumSize set to zero – Sam Aug 08 '11 at 16:47
  • You are not mistaken, but this will decrease overhead in this particular case rather dramatically because the allocation is already a fixed power of 2 > 4096. – Joshua Aug 08 '11 at 18:56