Huge memory usage on some machines

Hi yet again

I have weird problem with my DesktopGL game. On some machines my content loading eats huge amount of memory (2-3GB) leading to System.OutOfMemoryException. Its hard to debug because on my hardware it uses only 200-300MB. Problem might be related to culture/location differences.
Tester was running Windows 7 (I think), on ~5 years old laptop, with 8GB Ram, i7 26xx and HD6700M series.

I’m not sure but it seems that game loads initial content smoothly, but when I start going through Tiled maps (I use Tiled Map Editor for level design) it starts eating memory.

I use https://github.com/dmanning23/TiledSharp/blob/master/Tiled.cs for loading Tiled files, and have modified it a little bit. Loading uses a lot of TryParse and TryGetValue. To avoid problems caused by culture related settings, I use

CultureInfo ci = (CultureInfo)CultureInfo.CurrentCulture.Clone();
ci.NumberFormat.CurrencyDecimalSeparator = ".";

and load values with lines like

int.TryParse(reader.GetAttribute("tilewidth"), NumberStyles.Any, ci, out result.FirstTileID);
float.TryParse(reader.GetAttribute("opacity"), NumberStyles.Any, ci, out result.Opacity);

I’m not 100% sure if the problem is in the Tiled loading, but because the game shows Loading Screen it cannot be in my first Content Loader function that loads Textures, Font and Sounds.

Game currently uses MonoGame 3.7.0.213.

Any help is appreciated :slight_smile:

Hum… What gives a memory profiling ?

Can you catch the exception in program.cs or the lowest possible level, and put the stacktrace into a file ? So this user would be able to send it to you ?

1 Like

oh wow, I haven’t looked at that code in a long time… Sorry, it is definitely a total mess in there, I’ll clean it up and see if anything pops out.

1 Like

Yeah, it’s doing a bunch of low-level stuff with XmlReaders and MemoryStreams that aren’t being cleaned up :frowning:

1 Like

Interesting, as I’ve just begun implementing TiledSharp. However, I’m currently calling it during the Game1 Initialize function and not running into any issues so far. At some point the plan is to move the calls elsewhere (when loading each individual level after they are selected by the player).

As an aside, do I need to be concerned about CultureInfo for my .tmx files? I’m not planning on implementing any type of culture formatting into the game (it doesn’t need it) and I noticed the .txm file has culture set to “netural”, so I’m guessing it will keep it’s formatting and TiledSharp will be able to parse it no matter which region setting the PC running it is set to. Is that correct?

1 Like

So something like this?

using (var reader = XmlReader.Create(stream, settings))
{
    while (reader.Read())
    {
        //Code
    }

    ((IDisposable)reader).Dispose();
    reader.Close();
}

My game crashed on my friend’s laptop with Asian (Japanese?) settings on computer, but after I made those Culture and NumberStyle changes to Tiled Loading the game loaded on his computer. Weird thing is, he also has the same memory usage problem but after few minutes of playing memory usage goes down to normal levels, without crashing the game.

Interesting. I guess what I’m asking is will a PC with a different Culture Setting (I’m in the U.S.) change the commas in the .tmx file I created in Tiled to something else (most likely a period I would assume)? This would break TiledSharp’s parsing of the file, but I don’t the Culture Settings would change the file included in my application.

Was TiledSharp crashing while parsing the actual map layout or did you have other text fields you added (like object names and values) that were causing the crash?

Yeah by wrapping an IDisposable in a using statement, it calls Dispose() and automatically cleans up the memory when that object leaves scope. You don’t need to call Dispose() or Close() like that inside a using statement, I think it will actually throw an exception in that case.

I hunted through the TiledSharp code a little bit, and didn’t spot any places where disposables aren’t wrapped in using statements. This code definitely has “code stink” to high heaven though, it is painful to look at :frowning:

If it is allocating a ton of memory up front and then evening out later, that sounds like the garbage collector catching up… Have you tried calling GC.Collect() right after you load the map? It’ll drag the game to a screeching halt, but that’s usually ok in a loading screen.

IDisposable is used for unmanaged resources. Memory is managed in C#, so unless you’re using some third party dll that requires disposing things to clear out memory, it’s not the disposable assets. Maybe check if there are lists that you keep adding elements to without removing any, or stuff like that. There are plugins that can help find where you allocate stuff to the heap, that might help figuring out the issue.

When reading and writing to a data file, ALWAYS use CultureInfo.InvariantCulture.

I just took a look at TiledSharp’s source code and it uses CultureInfo.InvariantCulture in one particular spot (setting X and Y values for each Object in an Object Layer), but nowhere else. I’m at work now, but going to dig into this more over the weekend and update the parsing if/where necessary.

I just did some digging and I believe the following should resolve any reading/parsing issues when using TiledSharp to import Tiled maps with respect to Culture settings:

static void SwitchExample() {
    CultureInfo originalCulture = Thread.CurrentThread.CurrentCulture;
    Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
    try {
        //Parse .tmx file using TiledSharp
    }
    finally {
        Thread.CurrentThread.CurrentCulture = originalCulture;                
    }
}