Jump to content
  • Advertisement
Sign in to follow this  
RobMaddison

Who ate all the memory?

This topic is 986 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Does anyone know if there are any caveats or gotchas when allocating lots of heap memory in a native c++ dll from c# via C++/CLI?  Running my game editor (in release mode outside the debugger) seems to chew up close to 5GB for my 8192x8192 terrain.  When running the game, which is an all-native exe loading the native engine as a dll the same terrain uses around 500MB which is as I'd expect.

 

The 8192x8192 at the lowest quadtree level creates 65536 patch objects which are (currently) around 272 bytes each (approximately 18MB in total).  At the moment I'm not reserving this upfront so I'm wondering whether c# is forcing the native library to align the allocations to a much bigger order.  I'm a bit unclear as to why that would happen but it's the only thing I can think of at the moment.  Seems odd that it only uses 500MB when run in full native mode.

Share this post


Link to post
Share on other sites
Advertisement

Running my game editor (in release mode outside the debugger) seems to chew up 5GB ...  all-native exe loading the native engine as a dll the same terrain uses 500MB...

 

 

Are you absolutely certain you are using the release-mode memory libraries in the editor?   Have you verified that the process isn't loading debug memory tools?  The debug heap can cause memory use to explode when it uses the CRT Debug heap.  There is a lot of debug information available if you are using the debug heap.  You might have accidentally caused it to get linked in.

 

Are you using managed strings or similar GC'd objects? (e.g. String^ myString) The marshalling between managed and native can quickly add a lot of stuff to the global heap.  If you are managing it correctly it can add up quickly but the garbage will eventually be cleaned.  If you are managing it incorrectly or not properly releasing locked memory or have other marshalling issues, it can quickly consume an incredible amount of memory.

Share this post


Link to post
Share on other sites

Without thorough analyzing of the program it is hard to say.

Languages like C# are designed around the notion that RAM is unlimited or nearly unlimited, thus skyrocketing memory usage when dealing with large objects like a 8092x8092 texture doesn't surprise me. One wrong usage and you can get multiple duplicates (assuming there are no leaks).

 

Best advice I can give is to start removing code until you see a major change in RAM usage to narrow the search locate the offending snippets code, so it can be better analyzed.

Share this post


Link to post
Share on other sites

 

Running my game editor (in release mode outside the debugger) seems to chew up 5GB ...  all-native exe loading the native engine as a dll the same terrain uses 500MB...

 

 

Are you absolutely certain you are using the release-mode memory libraries in the editor?   Have you verified that the process isn't loading debug memory tools?  The debug heap can cause memory use to explode when it uses the CRT Debug heap.  There is a lot of debug information available if you are using the debug heap.  You might have accidentally caused it to get linked in.

 

Are you using managed strings or similar GC'd objects? (e.g. String^ myString) The marshalling between managed and native can quickly add a lot of stuff to the global heap.  If you are managing it correctly it can add up quickly but the garbage will eventually be cleaned.  If you are managing it incorrectly or not properly releasing locked memory or have other marshalling issues, it can quickly consume an incredible amount of memory.

 

Thanks, Frob

 

I've just been through all the settings for each project and it looks like I'm linking in the correct libraries. Running depends.exe on both the native c++ engine DLL and the c++/cli shim dll shows that I'm only pulling in MSVCRT.DLL, not MSVCRTD.DLL.

 

I am using String^ types, but I'm managing that marshaling correctly I believe and there are no calls using String^ types when I create the terrain.

Share this post


Link to post
Share on other sites

Without thorough analyzing of the program it is hard to say.

Languages like C# are designed around the notion that RAM is unlimited or nearly unlimited, thus skyrocketing memory usage when dealing with large objects like a 8092x8092 texture doesn't surprise me. One wrong usage and you can get multiple duplicates (assuming there are no leaks).

 

Best advice I can give is to start removing code until you see a major change in RAM usage to narrow the search locate the offending snippets code, so it can be better analyzed.

 

The odd thing is that the interaction between the c# side and the native c++ engine side is intentionally very limited.  The c# side knows nothing about terrains or how they're made or anything about the internal classes and/or objects of the native c++ engine, all it knows is that it can call the method AddTerrain(8192); on the sceneEditor wrapper object - the engine does the rest.

Share this post


Link to post
Share on other sites

Unfortunate.

 

Not fun, but time to do some poking around.  Find out what is actually stored in that 5GB chunk of memory.  Maybe some breakpoints to monitor what is doing all the allocations. 

 

If you can remove chunks of code as Matias Goldberg suggested that could help, but with that much memory it shouldn't be too hard to identify patterns.  Look for allocations at unexpected times, look for data that is locked but never unlocked. 

 

A few strategic breakpoints, such as to check memory usage before and after your call to AddTerrain(), and breakpoints on all memory allocators after you call AddTerrain, should isolate the allocations fairly quickly.  

Share this post


Link to post
Share on other sites

Unfortunate.

 

Not fun, but time to do some poking around.  Find out what is actually stored in that 5GB chunk of memory.  Maybe some breakpoints to monitor what is doing all the allocations. 

 

If you can remove chunks of code as Matias Goldberg suggested that could help, but with that much memory it shouldn't be too hard to identify patterns.  Look for allocations at unexpected times, look for data that is locked but never unlocked. 

 

A few strategic breakpoints, such as to check memory usage before and after your call to AddTerrain(), and breakpoints on all memory allocators after you call AddTerrain, should isolate the allocations fairly quickly.  

 

The code branch that causes the issue is fairly self-contained in itself, it is an iterative method that constructs the quadtree.  Within the method, there are 3 new allocations, one to create an entity for the node (at all levels in the tree).  That entity then gets a terrain patch component and a renderable component for those nodes low enough in the tree to be rendered.  For an 8192 x 8192 terrain, there are around 87k of these entities created.

 

I've overridden new and logged out all the new calls for this method including the sizes, file and line and the report is as expected with no huge allocations.  Debugging this method actually increases memory more than release mode (obviously), it's more like 8GB.  It's fairly easy to step through this code, but I can't see anything untoward going on.  I'm wondering whether the taskmanager perhaps isn't the best gauge of how much memory is actually being allocated.

Share this post


Link to post
Share on other sites

I've overridden new and logged out all the new calls for this method including the sizes, file and line and the report is as expected with no huge allocations.

 

If that didn't trap it, there are more ways to find it.

 

There are many ways to allocate memory. Most programs have multiple heaps and multiple ways the built-in libraries will allocate and manipulate pages of memory.  Ideally your program will use a consistent pattern, but when it doesn't, there are more tools available. The most simple is going to be a memory profiler.
 
There are many .net memory profilers out there. Many versions of Visual Studio come with one integrated, but I don't think it ships with the free versions.
 
The CLR Profiler lets you peek at memory, free profilers for CLR 2 (meaning .net 2.0, 3.0, and 3.5) and CLR 4 (meaning .net 4, 4.5, and 4.6).  The hardest part will be recording a profile of your program, which tends to be slow and painful.  Once you've recorded it, open the profile and view the allocation graph, then pick the context menu "Show Who Allocated".  You are going to have a giant block (or a huge number of tiny blocks) for whatever function allocated that multi-gigabyte chunk of memory.
 
If you don't want Microsoft's free one, use another. As ChaosEngine mentioned, dotMemory is a good one, has a 5 day trial period.  MemProfiler is another with free trial use.  Or Google to find more.  

Share this post


Link to post
Share on other sites

The way C# deals with memory is totally different than C++. When dealing with purely managed code, you never have to worry about releasing memory: the Garbage Collectors deals with it automatically. As a side effect, you might "see" memory usage being higher than it really is for the simple reason that it decided to not release some objects yet.

 

To eliminate this scenario, you can force the garbage collector to run. It's not recommended in production mode, but in a debug case like this, I'd recommend running it before analyzing memory to only see what is "actually" being used.

 

http://stackoverflow.com/questions/4257372/how-to-force-garbage-collector-to-run

 

With C#, the only time you have to worry about memory is when calling unmanaged code (C++). In this case, make sure you properly call the methods to release the objects, as C# cannot do it automatically for unmanaged objects.

 

Then, is the memory usage simply high, or is there a memory leak causing it to keep going higher? That's a big distinction that will help you look at the right place.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!