2

My issue is that I have a 32-bit DLL which I cannot modify. The DLL can sometimes require around 1.5 gigs of memory or so under normal operation.

When I use a C++/unmanaged code test program, the DLL will only go out of memory at about 2 gigs. This is expected as this is the maximum size available to a 32-bit process. Hence, the DLL runs fine under normal operation.

When I P/Invoke the DLL from a C# application that itself takes about 250 MB, the DLL errors out when the entire process reaches about 1.4 gigs. My question is does P/Invoke provide less memory for the 32 bit process to use? Is there some way I can give it more?

EDIT: The P/Invoked functions are actually called from a C# assembly referenced by my main assumbly. Not sure if this is relevant in any way.

Jas Laferriere
  • 804
  • 10
  • 12

2 Answers2

3

The difference is probably because your managed program has greater needs than the unmanaged program. And so there are fewer resources left for your DLL. Programs can run out of memory even when there appears to be memory left. This happens when the address space is fragmented and the virtual memory allocator cannot find a large enough contiguous block of memory to meet an allocation request.

There's not much you can do about that I suspect. I doubt you'll have much luck trying to make your managed process consume fewer resources. If you run on a 64 bit system and your process is large address aware then your process can access have 4GB of memory.

Marking your .net executable as LARGEADDRESSAWARE might well help. For details please refer to another SO question: How to make a .NET application "large address aware"? In summary you need to run the EDITBIN tool to mark your executable:

EDITBIN /LARGEADDRESSAWARE YourExeName.exe

Be aware though that on a 32 bit OS you are pretty much stuck with the 2GB limit. Yes you can boot your system with the /3GB switch but this is generally not to be advised.

The other obvious option would be to house the DLL in an out of process COM server. By putting it in a different process you could keep the .net runtime out of the process and leave as much space as possible for the hungry DLL.

Community
  • 1
  • 1
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • It is being run on a 64-bit machine but the DLL is 32-bit. I could make my project 64-bit but as far as I know this means I cannot access the 32-bit DLL. Also, the 1.4 gigs I mentioned is the total memory of the managed application + process (They show up combined in task manager) so it still doesn't reach 2 gigs combined. – Jas Laferriere Dec 06 '12 at 15:19
  • @Jasmin I guess there's memory fragmentation going on which is why it fails at 1.4GB. There may be some mileage in seeing whether or not you can mark your managed app as `LARGEADDRESSAWARE`. – David Heffernan Dec 06 '12 at 15:22
  • Thanks a lot for the brief explanation on memory fragmentation. Might be that's what is happening. LARGEADDRESSAWARE does not seem to be helping - may have to try your other solution. Do you know if there might be some benefit in making a CLI/C++ wrapper DLL instead of doing direct P/Invokes from C# in this case? – Jas Laferriere Dec 06 '12 at 15:52
  • No, that won't help. P/invoke isn't remotely resource heavy. It's the rest the .net beast that will be the problem. And you can't avoid that. I'm surprised /LARGEADDRESSAWARE does not help. – David Heffernan Dec 06 '12 at 15:53
  • Using LARGEADDRESSAWARE makes a big different for me. I wrote a simple console app that allocated 32MB byte[] objects. Without LARGEADDRESSAWARE I hit OOM at 1.4GB. With LARGEADDRESSAWARE it OOM at 2.7GB. Are you sure you didn't re-compile after calling EDITBIN? Remember that the call to EDITBIN has to be **after** the exe is built. – David Heffernan Dec 06 '12 at 15:59
  • In fact I just tested it again. I ensured the flag was set using dumpbin /headers and did a comparison with it set and with it not set. Seems like it might be very slightly better with it set - only appears to crash around 1.6 gigs whereas without it set it was crashing around 1.5 gigs. Not enough to warrant a celebration. It might just be that whatever I'm doing in my .NET application is fragmenting memory a lot and the DLL wants very large blocks? I do notice the DLL grabbing 150+MB chunks at a time it seems like. – Jas Laferriere Dec 06 '12 at 16:09
  • Actually now that I look it appears that the DLL is returning something it considers a "Calculation error" with the flag set whereas with the flag not set it returns a "Memory error". This is potentially good news. Thank you. – Jas Laferriere Dec 06 '12 at 16:22
1

this might happen because you c++ aplication has the LARGEADDRESSAWARE option set and the c# app doesnt

in c++ app it is defined here -> Linker->System-> Enable Large Addresses = "Yes/LARGEADDRESSAWARE"

try adding the following post build event

call "$(DevEnvDir)..\..\vc\vcvarsall.bat" x86
"$(DevEnvDir)..\..\vc\bin\EditBin.exe" "$(TargetPath)"  /LARGEADDRESSAWARE
makc
  • 2,569
  • 1
  • 18
  • 28