JDK-8133193 : Memory leak in G1 because G1RootProcessor doesn't have desctructor
  • Type: Bug
  • Component: hotspot
  • Sub-Component: gc
  • Affected Version: 9
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2015-08-07
  • Updated: 2016-01-14
  • Resolved: 2015-08-12
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 8 JDK 9
8u72Fixed 9 b81Fixed
Related Reports
Relates :  
Description
G1RootProcessor constructor allocate SubTasksDone object for _process_strong_tasks field(hotspot/src/share/vm/gc/g1/g1RootProcessor.cpp):

G1RootProcessor::G1RootProcessor(G1CollectedHeap* g1h, uint n_workers) :
    _g1h(g1h),
    _process_strong_tasks(new SubTasksDone(G1RP_PS_NumElements)),
    _srs(n_workers),
    _lock(Mutex::leaf, "G1 Root Scanning barrier lock", false, Monitor::_safepoint_check_never),
    _n_workers_discovered_strong_classes(0) {}

SubTasksDone also allocate memory(hotspot/src/share/vm/gc/shared/workgroup.cpp):

SubTasksDone::SubTasksDone(uint n) :
  _n_tasks(n), _tasks(NULL) {
  _tasks = NEW_C_HEAP_ARRAY(uint, n, mtInternal);
  guarantee(_tasks != NULL, "alloc failure");
  clear();
}   

But G1RootProcessor doesn't have destructor which will destroy SubTasksDone object when G1RootProcessor will go out from the scope. This leads to the memory leak in the following places:
hotspot/src/share/vm/gc/g1/g1CollectedHeap.cpp module:
void G1CollectedHeap::verify(bool silent, VerifyOption vo) {
...
    {
      G1RootProcessor root_processor(this, 1);
      root_processor.process_all_roots(&rootsCl,
                                       &cldCl,
                                       &blobsCl);
    }
...
void G1CollectedHeap::evacuate_collection_set(EvacuationInfo& evacuation_info) {
...
  {
    G1RootProcessor root_processor(this, n_workers);
    G1ParTask g1_par_task(this, _task_queues, &root_processor, n_workers);
    // InitialMark needs claim bits to keep track of the marked-through CLDs.
    if (collector_state()->during_initial_mark_pause()) {
      ClassLoaderDataGraph::clear_claimed_marks();
    }

    // The individual threads will set their evac-failure closures.
    if (PrintTerminationStats) {
      G1ParScanThreadState::print_termination_stats_hdr();
    }

    workers()->run_task(&g1_par_task);
    end_par_time_sec = os::elapsedTime();

    // Closing the inner scope will execute the destructor
    // for the G1RootProcessor object. We record the current
    // elapsed time before closing the scope so that time
    // taken for the destructor is NOT included in the
    // reported parallel time.
  }
...

hotspot/src/share/vm/gc/g1/g1MarkSweep.cpp module:
void G1MarkSweep::mark_sweep_phase1(bool& marked_for_unloading,
                                    bool clear_all_softrefs) {
...
  {
    G1RootProcessor root_processor(g1h, 1);
    root_processor.process_strong_roots(&GenMarkSweep::follow_root_closure,
                                        &GenMarkSweep::follow_cld_closure,
                                        &follow_code_closure);
  }
...
void G1MarkSweep::mark_sweep_phase3() {
...
  {
    G1RootProcessor root_processor(g1h, 1);
    root_processor.process_all_roots(&GenMarkSweep::adjust_pointer_closure,
                                     &GenMarkSweep::adjust_cld_closure,
                                     &adjust_code_closure);
  }
...
Comments
noreg-hard justification: This is a small per-gc memory leak, impossible to detect with a regression test.
30-09-2015

Mikael, thank you! Not need it urgently, just interesting.
01-09-2015

Yes, I was planning to do the backport but I've been busy. Feel free to backport it if you need it urgently.
01-09-2015

Is there are plans to backport this fix to the 8u releases?
01-09-2015

This should probably be backported to the appropriate 8u release
12-08-2015

It may be better, if possible to embed SubTasksDone directly into G1RootProcessor. Then the default destructor of G1RootProcessor should call the destructor of the SubTasksDone instance.
07-08-2015