Live data from Hacker News

Conservative GC: Is It Really That Bad?

excelsiorjet.com

1–10 of 48 posts

Re: Conservative GC: Is It Really That Bad?

#2
This is just off the top of my head, but it made me wonder: are there any VMs that put a stack map header of some sort as a literal in the stack?

E.g. for each frame the compiler orders roots first and then other primitives. Then, as you enter the frame, write the number of roots to the stack. When the GC walks the stack it can see precisely which are roots.

Re: Conservative GC: Is It Really That Bad?

#3

This is just off the top of my head, but it made me wonder: are there any VMs that put a stack map header of some sort as a literal in the stack? E.g. for each frame the compiler orders roots first and then other primitives. Then, as you enter the frame, write the number of roots to the stack. When the GC walks the stack it can see precisely which are roots.

That could be done, but generally precise collectors keep a fixed set of tables outside the stack, that way they only need to figure out which method frames are on the stack and then consult those tables. That incurs fewer writes.

Re: Conservative GC: Is It Really That Bad?

#4
The problematic code in the article is, AFAICT, using an object finalizer to free manually allocated memory; such approaches seldom work well, even with precise GCs.

Thread stacks are effectively manually allocated blocks of memory. You create a thread, which allocates the stack, and as long as the thread lives, the stack is kept alive - it's self-sustaining. The thread must die by explicit programmatic action, which in turn will free its allocated block of stack memory.

Using finalizers at all is usually an anti-pattern in a GC world. The presence of finalizers is a very strong hint that the GC is being used to manage resources other than memory, something that GC is a poor fit for, because other resources almost certainly have no necessary correlation with memory pressure; and GCs usually only monitor GC heap memory pressure.

That's not to say that there aren't plenty of edge cases where you can end up with lots of false roots that artificially lengthen object lifetimes with a conservative GC. Putting a thread stack in your cycle of object references and relying on GC pressure to break the cycle isn't a strongly motivating one to my mind, though.

Re: Conservative GC: Is It Really That Bad?

#5

This is just off the top of my head, but it made me wonder: are there any VMs that put a stack map header of some sort as a literal in the stack? E.g. for each frame the compiler orders roots first and then other primitives. Then, as you enter the frame, write the number of roots to the stack. When the GC walks the stack it can see precisely which are roots.

The problem is that it all depends on the liveness of the variables. The same value on the stack can be a root at the begining of the method as the corresponding variable is still alive, and later it becomes useless as the variable is already dead. So, you still need to know a location of each live reference at every safepoint (and that means several stack-maps for each method).

Re: Conservative GC: Is It Really That Bad?

#6
post #5

This is just off the top of my head, but it made me wonder: are there any VMs that put a stack map header of some sort as a literal in the stack? E.g. for each frame the compiler orders roots first and then other primitives. Then, as you enter the frame, write the number of roots to the stack. When the GC walks the stack it can see precisely which are roots.

The problem is that it all depends on the liveness of the variables. The same value on the stack can be a root at the begining of the method as the corresponding variable is still alive, and later it becomes useless as the variable is already dead. So, you still need to know a location of each live reference at every safepoint (and that means several stack-maps for each method).

You can zero them when they are not live.

Re: Conservative GC: Is It Really That Bad?

#7
post #4

The problematic code in the article is, AFAICT, using an object finalizer to free manually allocated memory; such approaches seldom work well, even with precise GCs. Thread stacks are effectively manually allocated blocks of memory. You create a thread, which allocates the stack, and as long as the thread lives, the stack is kept alive - it's self-sustaining. The thread must die by explicit programmatic action, which…

Absolutely agree with you about finalizers! However, please note that this "threadReaper" code is from JDK class, so, the problem can appear on every application that just use Timer class.

Of course, there are many other examples of false-roots, but this concrete class caused unexpected OOMs on several applications of our clients, so we made this small sample and used it for sanity checking during implementing precise GC (and then mentioned it in the post).

Re: Conservative GC: Is It Really That Bad?

#8
post #4

The problematic code in the article is, AFAICT, using an object finalizer to free manually allocated memory; such approaches seldom work well, even with precise GCs. Thread stacks are effectively manually allocated blocks of memory. You create a thread, which allocates the stack, and as long as the thread lives, the stack is kept alive - it's self-sustaining. The thread must die by explicit programmatic action, which…

This is right: finalizers are difficult to combine with garbage collection for several reasons. As you say, there's no guarantee that the garbage collector will find the finalizable object in a timely manner. Additionally, if it's a conservative collector, then the finalizable object may be pinned by an ambiguous reference and the collector will be unable to free it. In multi-threaded programs the collector runs asynchronously from the point of view of the code, so if the garbage collector calls the finalizer then the finalization code may not be able to depend on invariants of data structures. It is hard to write correct finalization code under these conditions.

A 2002 technical report by Hans Boehm discusses these difficulties. http://www.hpl.hp.com/techreports/2002/HPL-2002-335.html

Re: Conservative GC: Is It Really That Bad?

#9
post #5

Earlier quoted context omitted.

The problem is that it all depends on the liveness of the variables. The same value on the stack can be a root at the begining of the method as the corresponding variable is still alive, and later it becomes useless as the variable is already dead. So, you still need to know a location of each live reference at every safepoint (and that means several stack-maps for each method).

You can zero them when they are not live.

Yeah, we've tried that. It was one of the attempts of improving conservative GC (no dead values on the stack => no false roots). Unfortunately, it causes noticeable performance degradation, so it is easier to consult with stack maps about liveness of the variable.

Re: Conservative GC: Is It Really That Bad?

#10
I am not an expert in garbage collection techniques, but this article does not even mention locality of reference (copying GCs improve locality on each compaction) and how many cache misses are introduced by increased fragmentation. Are there any benchmarks on this?
Post reply on HN