Asked  7 Months ago    Answers:  5   Viewed   36 times

As I understand it there's a 2 GB limit on single instances in .NET. I haven't paid a lot of attention to that since I have mainly worked on 32 bit OS so far. On 32 but it is more or less an artificial limitation anyway. However, I was quite surprised to learn that this limitation also applies on 64 bit .NET.

Since collections such as List<T> use an array to store items, that means that a .NET application running on 32 bit will be able to hold twice as many reference type items in a list compared to the same application running on 64 bit. That is quite surprising imo.

Does anyone know if this limitation is addressed in CLR 4.0 (I don't have a 4.0 installation at hand at the moment).

 Answers

95

It's worse than that - you're process space, when you're working in .NET in 32bit is much smaller than the theoretical limit. In 32bit .NET apps, my experience is that you'll always tend to start getting out of memory errors somewhere around 1.2-1.4gb of memory usage (some people say they can get to 1.6... but I've never seen that). Of course, this isn't a problem on 64bit systems.

That being said, a single 2GB array of reference types, even on 64bit systems, is a huge amount of objects. Even with 8 byte references, you have the ability to allocate an array of 268,435,456 object references - each of which can be very large (up to 2GB, more if they're using nested objects). That's more memory than would ever really be required by most applications.

One of the members of the CLR team blogged about this, with some options for ways to work around these limitations. On a 64bit system, doing something like his BigArray<T> would be a viable solution to allocate any number of objects into an array - much more than the 2gb single object limit. P/Invoke can allow you to allocate larger arrays as well.


Edit: I should have mentioned this, as well - I do not believe this behavior has changed at all for .NET 4. The behavior has been unchanged since the beginning of .NET.


Edit: .NET 4.5 will now have the option in x64 to explicitly allow objects to be larger than 2gb by setting gcAllowVeryLargeObjects in the app.config.

Tuesday, June 1, 2021
 
Optimus
answered 7 Months ago
15

The maximum size of an object HotSpot JVM may allocate in young generation is nearly as large as the size of Eden (YoungGen minus two Survivor spaces).

That's how the allocation rougly looks like:

  1. Use Thread Local Allocation Buffer (TLAB), if tlab_top + size <= tlab_end
    This is the fastest path. Allocation is just the tlab_top pointer increment.
  2. If TLAB is almost full, create a new TLAB in Eden and retry in a fresh TLAB.
  3. If TLAB remaining space is not enough but is still to big to discard, try to allocate an object directly in Eden. Allocation in Eden is also a pointer increment (eden_top + size <= eden_end) using atomic operation, since Eden is shared between all threads.
  4. If allocation in Eden fails, a minor collection typically occurs.
  5. If there is not enough space in Eden even after Young GC, an attempt to allocate directly in Old generation is made.
Thursday, June 10, 2021
 
LOKESH
answered 6 Months ago
63

Yes, the CLR version is the same, still v4.0.30319. What broke in a highly backwards-breaking way were the .NET Framework class libraries. Changes that were driven by integrating support for WinRT (Store apps) into the framework. Several types were moved from one assembly to another, the most visible ones are ExtensionAttribute and ICommand. The need for small .NET implementations on mobile devices was instrumental. System.Core and PresentationFramework are not small.

That's rather a big deal, the assembly that a type is declared in is part of the type identity of a .NET type. Or to put it another way, a .NET type that has the same namespace name and type name is never compatible with a type with the same full name that came from another assembly.

That this was possible at all was due to innovations that started in .NET 4.0. Starting with the [TypeForwardedTo] attribute which is what is used to move a type. And the specially crafted reference assemblies in c:program filesreference assemblies. Special in that they only contain metadata and are not a direct match with the actual assemblies installed in the GAC that your program will use at runtime.

This does go wrong sometimes in a very hard to diagnose ways, induced by using the wrong reference assemblies. Unfortunately Microsoft kept the traditional reference assemblies in c:windowsmicrosoft.netframework around. Projects that use them fail in a pretty miserable way.

So this just can't work, a program that targets .NET 4.5 will look in the wrong 4.0 GAC assemblies for types like ExtensionAttribute and ICommand. And fail with a utterly undiagnosable TypeLoadException. Accordingly, the [TargetFramework] attribute is checked first to fail the attempt to run the program as early as possible.

Thursday, July 22, 2021
 
chugadie
answered 5 Months ago
76

There are no set thresholds, they dynamically change as the garbage collector learns more about the program's allocation pattern. There is no way for you to discover the current threshold, nor to change it. From casual observation, it appears workstation GC starts out with a 2 megabyte gen 0 heap. Which can grow to over 8 megabytes.

Server GC is quite different from workstation, it normally uses larger thresholds and multiple threads to collect garbage. Again, nothing you could discover, other than by observing the performance counters in Perfmon.exe

Monday, August 23, 2021
 
RustyFluff
answered 4 Months ago
94

Your MSVS versions are quite old, so based on that, and assuming they default to C++03, they are correct to reject your code. I'll quote n1905, which for our purposes is pretty close to the C++03 standard.

9.4 [class.static] (emphasis mine)

If an unqualified-id (5.1) is used in the definition of a static member following the member’s declarator-id, and name lookup (3.4.1) finds that the unqualified-id refers to a static member, enumerator, or nested type of the member’s class (or of a base class of the member’s class), the unqualified-id is transformed into a qualified-id expression in which the nested-name-specifier names the class scope from which the member is referenced. The definition of a static member shall not use directly the names of the non-static members of its class or of a base class of its class (including as operands of the sizeof operator). The definition of a static member may only refer to these members to form pointer to members (5.3.1) or with the class member access syntax (5.2.5).

Monday, August 30, 2021
 
Jon
answered 4 Months ago
Jon
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share