ChatGPT解决这个技术问题 Extra ChatGPT

.Net vs Java Garbage Collector

Does anyone know the major differences between the Java and .Net garbage collectors? A web search has not revealed much, and it was a question that came up in a test.


B
Buhake Sindi

The difference is between the CLR (.Net) GC and the JVM GC rather than the languages themselves. Both are subject to change and the specification of their behaviour loose to allow this to be changed without it affecting the correctness of programs.

There are some historical differences largely due to .Net being designed with lessons from the evolution of the java (and other gc based platforms). In the following do not assume that the .Net one was in some way superior because it included functionality from the beginning, it is simply the result of coming later.

A notable publicly visible difference is that the MS GC exposes its generational nature (via the GC api) this is likely to remain true for some time since this is an obvious approach to take based on the behaviour that most programs exhibit: Most allocations are extremely short lived.

Initial JVM's did not have generational garbage collectors though this feature was swiftly added. The first generational collectors implemented by SunOracle and others tended to be Mark and Sweep. It was realized that a mark-sweep-compact approach would lead to much better memory locality justifying the additional copying overhead. The CLR runtime debuted with this behaviour.

A difference between SunOracle's and Microsoft's GC implementation 'ethos' is one of configurability.

Sun's provides a vast number of options (at the command line) to tweaks aspects of the GC or switch it between different modes. Many options are of the -X or -XX to indicate their lack of support across different versions or vendors. The CLR by contrast provides next to no configurability; your only real option is the use of the server or client collectors which optimise for throughput verses latency respectively.

Active research in GC strategies is ongoing in both companies (and in open source implementations) current approaches being used in the most recent GC implementations are per thread eden areas (improving locality and allowing the eden collection to potentially not cause a full pause) as well as pre-tenuring approaches, which try to avoid placing certain allocations into the eden generation.


Very good answer. Just the facts - nicely avoiding the religious war.
would the -1 care to add a reason?
It's worthwhile to note that "normal" .net uses a technique called a "write fence" to keep track of which objects have been written since the last time they were promoted. From what I understand, if an object got promoted from e.g. gen0 to gen1, and hasn't been written since then, it cannot possibly contain any references to gen0 objects; during a gen0 collection, there's no need to examine any references contained therein since the only objects it references are going to be presumed "live" anyway. Skipping non-written gen1/gen2 objects during a gen0 collection is a big win.
@supercat I would expect most (all?) java generational GC's to use the same technique to be honest, I can't see any feature of java that would preclude the use of it.
@ShuggyCoUk: I believe some do, but wouldn't be surprised if some do not, since write fences are not free, and since generational GC can offer some benefit, at very low cost, without them. If I were designing a framework, I would provide a means of specifying that certain classes were immutable (including an immutable-array type); even implementations which did not support write fences could benefit from knowing that a gen1 immutable object wasn't going to hold any references to any gen0 objects.
B
Brian Rasmussen

This is just to add to ShuggyCoUk's excellent answer. The .NET GC also uses what is know as the large object heap (LOH). The CLR preallocates a bunch of objects on the LOH and all user allocated objects of at least 85000 bytes are allocated on the LOH as well. Furthermore, double[] of 1000 elements or more are allocated on the LOH as well due to some internal optimization.

The LOH is handled differently than the generational heaps in various ways:

It is only cleaned during a full collect and it is never compacted like the generational heaps.

Allocation from the LOH is done via a free list much like malloc is handled in the C runtime, whereas allocations from the generational heap is essentially done by just moving a pointer in generation 0.

I don't know if the JVM has something similar, but it is essential information on how memory is handled in .NET so hopefully, you find it useful.


Good point, Is it considered good form to integrate other's extensions into your own answer with an edit, if so should you credit inline or via a comment?
now it has an option to compact LOH which is off default.
The option to compact is "compact once". You turn it on and the next blocking Gen2 collection will compact the LOH. Once compacted, the option reverts to off/default.
J
Jason Baker

If I recall correctly, the JVM doesn't release deallocated memory back to the operating system as the CLR does.


This is one of the more prominent differences.
d
duffymo

Java 5 introduced a lot of changes into its GC algorithms.

I'm not a C# maven, but these two articles suggest to me that both have evolved away from simple mark and sweep and towards newer generation models:

http://java.sun.com/docs/hotspot/gc5.0/gc_tuning_5.html http://www.csharphelp.com/archives2/archive297.html


t
tobsen

I found this:

In the J2SE platform version 1.4.2 there were four garbage collectors from which to choose but without an explicit choice by the user the serial garbage collector was always chosen. In version 5.0 the choice of the collector is based on the class of the machine on which the application is started.

here and this

Also just as the JVM manages the destruction of objects so also does the CLR via a Mark and Compact garbage collection algorithm

here

I hope this helps...