.NET 4.0 has 2 Global Assembly Cache (GAC)

While reading an article on Understanding The CLR Binder, I found out that .NET 4.0 has 2 distinct GAC’s (Global Assembly Cache). In previous .NET versions, when I installed a .NET assembly into the GAC (using gacutil.exe), I could find it in the ‘C:\Windows\assembly’ path. With .NET 4.0, GAC is now located in the 'C:\Windows\Microsoft.NET\assembly’ path.

Note that previous versions of .NET continue using the same ‘C:\Windows\assembly’ path. The article says:

In .NET Framework 4.0, the GAC went through a few changes. The concept of placing assemblies into a global directory began in CLR v1.1. In case of .NET Framework 1.1 (which had CLR v1.1) and .NET Framework 2.0 (which had CLR 2.0), the GAC was split into two, one for each CLR. This avoided the leaking of assemblies across CLR versions. For example, if both .NET 1.1 and .NET 2.0 shared the same GAC, then a .NET 1.1 application, loading an assembly from this shared GAC, could get .NET 2.0 assemblies, thereby breaking the .NET 1.1 application.

The CLR version used for both .NET Framework 2.0 and .NET Framework 3.5 is CLR 2.0. As a result of this, there was no need in the previous two framework releases to split the GAC. The problem of breaking older (in this case, .NET 2.0) applications resurfaces in Net Framework 4.0 at which point CLR 4.0 released. Hence, to avoid interference issues between CLR 2.0 and CLR 4.0, the GAC is now split into private GACs for each runtime.”
Interference Issues? Hmm..Interesting although I am still pondering over the reason! So that means, the GAC split reasoning in a .NET 4.0 scenario could be re-quoted as “If both .NET 2.0 and .NET 4.0 shared the same GAC, then a .NET 2.0 application, loading an assembly from this shared GAC, could get .NET 4.0 assemblies, thereby breaking the .NET 2.0 application”

I am eager to try out a scenario to see it myself. The baseline is, you now have 2 GAC’s in .NET 4.0 and you will now have to manage each of them individually!

Any other reasons you can think of the .NET 4.0 GAC split? Feel free to share them in the comments section.

About The Author

Suprotim Agarwal
Suprotim Agarwal, Developer Technologies MVP (Microsoft Most Valuable Professional) is the founder and contributor for DevCurry, DotNetCurry and SQLServerCurry. He is the Chief Editor of a Developer Magazine called DNC Magazine. He has also authored two Books - 51 Recipes using jQuery with ASP.NET Controls. and The Absolutely Awesome jQuery CookBook.

Follow him on twitter @suprotimagarwal.


Scott Brickey said...

I'm not reading much difference in GAC behavior.

if you actually look into the file system behind the GAC (which requires either a command prompt to understand the folder structure, or drive mappings), Microsoft has always had a folder structure as:

where arch may be x86, x64, or MSIL.

The multiple versions allows me to publish 3 versions of the same assembly concurrently. This allows program A to use v1, program B to use v2, and program C to use v3.

This solves the DLL Hell problem which used to exist, in which case they would all be forced to use the same version, which inevitably would include incompatibilities.

So you ask why 3.0 and 3.5 used the 2.0 CLR? because it wasn't changing anything, it was building. DotNet 2 *REPLACED* a ton of functionality from 1.0/1.1 (anyone who dealt w/ 1.1 remembers the pains). 3.0's WCF/WPF/WWF weren't replacing anything from 1.0/1.1 or 2.0, and 3.5's LINQ/etc weren't replacing anything from 1.1/2.0/3.0.

Mark Miller said...

Thanks for the post. "Interference issues" was intentionally vague. At the time of writing, the issues were still being investigated, but it was clear there were several broken scenarios.

For instance, some applications use Assemby.LoadWithPartialName to load the highest version of an assembly. If the highest version was compiled with v4, then a v2 (3.0 or 3.5) app could not load it, and the app would crash, even if there were a version that would have worked. Originally, we partitioned the GAC under it's original location, but that caused some problems with windows upgrade scenarios. Both of these involved code that had already shipped, so we moved our (version-partitioned GAC to another place.

This shouldn't have any impact to most applications, and doesn't add any maintenance burden. Both locations should only be accessed or modified using the native GAC APIs, which deal with the partitioning as expected. The places where this does surface are through APIs that expose the paths of the GAC such as GetCachePath, or examining the path of mscorlib loaded into managed code.

It's worth noting that we modified GAC locations when we released v2 as well when we introduced architecture as part of the assembly identity. Those added GAC_MSIL, GAC_32, and GAC_64, although all still under %windir%\assembly. Unfortunately, that wasn't an option for this release.

Atul Gupta said...

This is interesting, though am not clear why say a %windir%\assembly\v4 option didn't work?

Also interestingly the path for .NET 4 GAC isn't integrated with the shell extension (ShFusion.dll) and hence doesn't provides the default look of what is seen in %windir%\assembly

For a roof on head said...

I guess this was the whole side by side execution base requirement - so not major deal as native gac apis access it.

Anonymous said...

2 Gac's? wow I never knew that! We are upgrading out .net 2.0 app to 4.0 and I will keep this point in mind. Thanks for this post!

- Greetings from Andrey of bulgaria

Lucian Schultz said...

Your articles it’s very useful for me a lot about the.NET 4.0 has 2 Global Assembly Cache (GAC), and I am completely satisfied with your Blog. All comments and articles are very useful and very good. Your blog is very attention-grabbing. This article is very informative. Thank you for sharing this article.