RFR (M/L): 8247928: Refactor G1ConcurrentMarkThread for mark abort (JDK-8240556)

stefan.johansson at oracle.com stefan.johansson at oracle.com
Fri Aug 21 08:20:59 UTC 2020


Hi Thomas,

On 2020-06-25 10:56, Thomas Schatzl wrote:
> Hi all,
> 
>    can I have reviews for this change that refactors the 
> G1ConcurrentMarkThread class to prepare it better for mark abort 
> (JDK-8240556).
> 
> So the idea in the latter is to abort the concurrent cycle if after 
> concurrent start gc we find that old gen occupancy actually went down 
> below the IHOP again due to e.g. eager reclaim. To do that, G1 needs to 
> scrub any marks on the next bitmap during the gc pause.
> The current, original change just performs this work in the pause, which 
> is very slow, so the idea is to do the bitmap cleaning concurrently 
> instead. The problem is that G1ConcurrentMarkThread is a mess and you 
> can't easily "jump" to the end of the current marking.
> 
> Additionally, the code was worth refactoring without that requirement 
> anyway ;)
> 
> This change refactors the code so that it is much easier to do add a 
> second path through the concurrent cycle.
> 
> Overall, there are two options to do that:
> 1) provide an explicit state machine for the concurrent marking so that 
> you can jump to the end easily.
> 2) provide building blocks that can be easily put together to implement 
> the second path.
> 
> While 1) works (there is a sample POC at 
> http://cr.openjdk.java.net/~tschatzl/8247928/webrev.sm/), I found that 
> it is much more code and less understandable than just building two 
> paths through the marking cycle as in the "abort marking" case we only 
> need to do the very tail end of the regular full marking cycle, so I 
> propose this option for review.
> 
> This is refactoring (almost) only, the additional path should be added 
> with JDK-8240556. Almost because I changed two things:
> - the concurrent mark (control) thread does not do any marking any more 
> for a long time (long long ago it did root scanning/marking), so I 
> removed the _vtime_mark_accum accumulator.
> - the "Concurrent Mark" finish message is now only printed at the end of 
> all marking, not every iteration. First, the restart case is very rare 
> so probably anyone parsing it will not handle this case correctly, and 
> the contents (times) of that finish message are confusing, which means 
> most likely anyone handling it will likely do the wrong thing.
> The "Concurrent Mark Restart for Mark Overflow" remains to indicate a 
> restart.
> 
> Based on JDK-8248221 also out for review.
> 
> CR:
> https://bugs.openjdk.java.net/browse/JDK-8247928
> Webrev:
> http://cr.openjdk.java.net/~tschatzl/8247928/webrev/
Thanks for taking on this task. As we discussed before, there are pros 
and cons with the different approaches but I agree with you that this 
solution is easy to follow and understand.

Generally this looks good, just a few comments:

src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp
---
  118 class G1ConcPhaseTimer : public 
GCTraceConcTimeImpl<LogLevel::Info, LOG_TAGS(gc, marking)> {
  119   G1ConcurrentMark* _cm;
  120   const char* _t;

The addition of _t is unused and should be removed.
---
  145     concurrent_cycle_start();
  146     full_concurrent_cycle_do();
  147     concurrent_cycle_end();

I think do_concurrent_cycle sounds better than full_concurrent_cycle_do, 
but if you prefer the current name I'm ok with it.
---
  253
  254   Ticks mark_start = Ticks::now();
  255   log_info(gc, marking)("Concurrent Mark (%.3fs)", 
mark_start.seconds());
  256
  ...
  278
  279   Ticks mark_end = Ticks::now();
  280   log_info(gc, marking)("Concurrent Mark (%.3fs, %.3fs) %.3fms",
  281                         mark_start.seconds(), mark_end.seconds(),
  282                         (mark_end - mark_start).seconds() * 1000.0);
  283

As we talked about offline I would like to group this part into a phase, 
to avoid timing in the middle of the main function. Something like this:
http://cr.openjdk.java.net/~sjohanss/8247928/rev1/

What do you and other reviewers think about this?
---
  271     // Phase 6: Remark pause
  272     if (phase_remark(needs_restart)) return;
  273     if (needs_restart) {
  274       log_info(gc, marking)("Concurrent Mark Restart for Mark 
Stack Overflow (iteration #%u)",
  275                             iter++);
  276     }

Not a big fan out out parameters, what do you think about instead 
wrapping _cm->has_overflown() in a function needs_restart()? If you want 
to avoid having to do two calls, we could make the loop run forever 
until needs_restart is false and break (or call the wrapper 
marking_completed() and break if it is true).
---

src/hotspot/share/gc/g1/g1ConcurrentMarkThread.inline.hpp
---
   38   // The G1ConcurrentMarkThread does not do any marking at all.
   39   return _cm->all_task_accum_vtime();

I think we can skip the comment, that is basically the justification to 
remove this, but going forward it isn't really needed.
---

Thanks,
Stefan

> Testing:
> tier1-5, a few local jtreg runs of the gc directory
> 
> Thanks,
>    Thomas


More information about the hotspot-gc-dev mailing list