Class unloading in ZGC

Erik Österlund erik.osterlund at
Mon Dec 7 10:35:26 UTC 2020

Hi Liang,

So there are two distict cases. Class unloading enabled (default), and 
class unloading disabled (seemingly
for people that just really want to have memory leaks for no apparent 
good reason).

When class unloading is enabled, the code cache comprises weak roots, 
except oops that are on-stack
that are treated as strong. These semantics are the same across all GCs. 
When marking starts, ZGC
lazily processes the snapshot of nmethods that were on-stack when 
marking started, with lazy application
of nmethod entry barriers. These barriers will mark the objects, and 
heal the pointers to the corresponding
marked color, as expected by our barrier machinery. New nmethods that 
are called go through the same
processing using nmethod entry barriers. Semantically this ensures that 
on-stack nmethods are treated
as strong roots, and the rest of the nmethods are treated as weak roots. 
This has the same semantics
as any other GC.

When class unloading is disabled, the code cache comprises strong roots. 
That means that the GC will
during concurrent marking walk all nmethods, and mark the oops as 
strong. However, remember that there
are two operations: marking the objects, and self-healing the pointers 
as expected by the barrier machinery.
The second part of the operation still requires us to lazily apply 
nmethod entry barriers to the stacks
as well as arming nmethod entry barriers for calls, during concurrent 
marking, so that the oops in the
nmethods are self-healed to the corresponding marked pointer color, 
before they are exposed to the
execution of mutators, which might for example store this oop into the 
object graph. So I suppose the
special thing here compared to G1 is that we both walk the code cache 
marking all the oops, *and* explicitly
walk the stacks marking them as well, with the main purpose of fixing 
the pointer colors before the mutator
gets to use the nmethod. And arming the nmethod entry barriers for 
calls, for the same reason.

During relocation, we only arm the nmethod entry barriers with and 
without class unloading. The relocation
is lazy and won't be performed until either someone uses the nmethod 
(on-stack lazy nmethod entry barrier
or a call to a new nmethod), or the subsequent marking cycle will walk 
the code cache and make sure that
the objects are remapped, when it is performing marking.

Hope this makes sense and sheds some light on this confusion.


On 2020-12-06 16:40, Liang Mao wrote:
> Hi ZGC team,
> Previously without concurrent class unloading in ZGC, the code cache will be all treated as strong
> roots. Then concurrent class unloading will only mark the nmethod of executing threads at mark start
> pause and use the nmethod entry barrier to heal and also mark the oops. That sounds reasonable. But
> when I looked into the concurrent marking in G1, it doesn't threat all code cache as strong roots and
> of course has no nmethod entry barrier. So I'm confused why ZGC need the nmethod entry barrier for
>   marking. Does the difference comes from the different algorithm of SATB vs load barrier?
> Thanks,
> Liang

More information about the zgc-dev mailing list