It's been a decade since 64-bit processors have started popping up in the consumer Windows environments. Today we are all running 64-bit OSes on our 64-bit CPUs. Using Managed Code in .NET is a very good way of developing software without having to worry about the bitness of the CPU and OS; just write it, compile it and it runs anywhere.
"If you have 100% type safe managed code then you really can just copy it to the 64-bit platform and run it successfully under the 64-bit CLR" - MSDN
Some Basics
When you install the .NET Framework on a 64-bit machine the package is bigger because you're getting both the 32-bit and the 64-bit versions. Some of the things that are 64-bit specific are:
- Base class libraries (System.*)
- Just-In-Time compiler
- Debugging support
- .NET Framework SDK
Platform Targets
In .NET, you can build your projects for Any CPU, x86, x64 and Itanium. By default, the Platform Target is Any CPU.
What Any CPU Meant
Before Visual Studio 2010, AnyCPU was the default for most .NET Projects[1]. In Visual Studio 2010, x86 (and not AnyCPU) became the default for most .NET projects. In the good old days (up to .NET 4.0), AnyCPU meant:
- If the process runs on a 32-bit OS, it runs as a 32-bit process. IL is compiled to x86 machine code.
- If the process runs on a 64-bit OS, it runs as a 64-bit process. IL is compiled to x64 machine code.
- If the process runs on an Itanium OS, it runs as a 64-bit process. IL is compiled to Itanium machine code.
What Any CPU Really Means As Of .NET 4.5
In .NET 4.5 and Visual Studio 11, the default for most .NET projects is again AnyCPU, but there is more than one meaning to AnyCPU now. There is an additional sub-type of AnyCPU (Any CPU 32-bit preferred), which is the new default. When using this flavor of AnyCPU, the semantics are the following:
- If the process runs on a 32-bit OS, it runs as a 32-bit process. IL is compiled to x86 machine code.
- If the process runs on a 64-bit OS, it runs as a 32-bit process. IL is compiled to x86 machine code.
- If the process runs on an ARM OS, it runs as a 32-bit process. IL is compiled to ARM machine code.
You may find it weird that even though you have a 64-bit OS, your process still starts as a 32-bit process. This is intentional and there are advantages of having your processes be 32-bit processes.
Why are 32-bit processes sometimes better than 64-bit processes?
If you run a 64-bit application on 64-bit Windows, the user address space is 8TB (another 8TB is set aside for kernel address space), even though the Garbage Collector might have a heart attack before then...
Does your web app need to address more than 4GB of memory from any single process? If not, then your web app probably doesn't need to be 64-bit!
In fact, in my case, as Matthew Steeples pointed out, for an application that is practically empty 99% just the .NET Framework, a 64-bit processes use about 50% more memory than their 32-bit equivalent. Let's try it out. I have created the ASP.NET 4.6 MVC Hello World template and deployed it onto a test server running Windows Server 2016 Technical Preview 4 with IIS. I deployed both applications in 32-bit and 64-bit modes and sent 1,000 requests to each application pool to warm them up.
We can see that the 64-bit process uses about 30MB more memory than the 32-bit web app. 64-bit is always going to be bigger than 32-bit. Always. So, again, we need to ask ourselves: does our web app need to address more than 4GB from a single process?
Conclusion
In the cloud-oriented environment we live in today, you can fit more 32-bit applications into an Azure App Service B1 instance.
Theoretically, if we were to deploy only the ASP.NET MVC Hello World Template on this Azure B1 instance, we could deploy 17 x 64-bit apps (€2.71/app/month) and 24 x 32-bit apps (€1.96/app/month).
And, again, as long as you work with managed code, you can start with 32-bit processes and then just switch to 64-bit whenever you are reaching your memory limits.
This was confusing to some developers: when they ran the application on a 64-bit Windows system, the process was a 64-bit process, which may cause unexpected results. For example, if the application relies on an unmanaged DLL of which only a 32-bit version is available, its 64-bit version won’t be able to load that component ↩︎