You’ve just created a Console app in the latest Visual Studio, and wrote some C# code that allocates some non-negligible quantity of memory, say 6 GB. The machine you’re developing has a decent amount of RAM – 16GB – and it’s running 64-bit Windows 10.
You hit F5, but are struck to find the debugger breaking into your code almost immediately and showing:
What’s going on here ? You’re not running some other memory-consuming app. 6 GB surely should have been in reach for your code. The question that this post will set out to answer thus becomes simply: “Why do I get a System.OutOfMemoryException when running my recently created C# app on an idle, 64-bit Windows machine with lots of RAM ?“.
TL;DR (small scroll bar => therefore I deduct a lot of text => I’ve got no time for that, and need the answer now): The default build settings for Visual Studio limit your app’s virtual address space to 4 GB. Go into your project’s Properties, go to Build, and choose Platform target as x64. Build your solution again and you’re done.
Not so fast ! Tell me more about what goes on under the hood: Buckle up, since we’re going for a ride. First we’ll look at a simple example of code that consumes a lot of memory fast, then uncover interesting facts about our problem, hit a “Wait, what ?” moment, learn the fundamentals of virtual memory, find the root cause of our problem then finish with a series of Q&A.
You’re working with assemblies in .NET, and you have to load one of these assemblies in your code. Trouble is that the code you’re currently running – and which is getting ready to load that assembly – has been built for the x64 architecture, and the assembly you want to load was built for x86. You try to load the assembly – either explicitly or by calling one of the methods in it – and sure enough, you run into the following exception:
System.BadImageFormatException: ‘Could not load file or assembly ‘x86_Assembly’ or one of its dependencies. An attempt was made to load a program with an incorrect format.’
Ok, so clearly this combination whereby an x86 assembly gets loaded in x64 code doesn’t work. But what about the rest ? There are 3 options in the Platform target drop down, and the Prefer 32-bit option becomes active once the Any CPU is selected, thus yielding a total of 4 possible targets.
Those 4 targets can equally apply to the assembly that’s being loaded, as well as to the code that’s loading it. All in all, 16 possible combinations. The question that this post sets out to answer is “Which out of these 16 work, and which don’t ?“.
TL;DR (I’ve got no time for your nonsense, just tell me the answer): The table of truth will tell you. If you make sure the assembly you’re loading is built using AnyCPU or AnyCPU+Prefer 32-bit, you’ll have no issues, regardless of the platform target you’re using for the “host” process, aka the code that loads the assembly. “Explicit cross-bitness”, as in loading x86 assembly in x64 code or the other way around won’t work. Having the same platform target for the code consuming the assembly and the assembly itself will always work. For “How about loading assemblies in an AnyCPU host process” or “Why is this ?” you’ll have to read on.
Give me the whole story (I’ve got plenty of popcorn, carry on): we’ll start by defining what an assembly is, what does it mean to load an assembly and how it can be done, present an example and analyze the outcome, and in the end go deeper and draw some conclusions. We’ll finish with a Q&A session.