Log examples

To help you understand how garbage collection (GC) processes memory for your application and how these processes are recorded, a number of annotated log examples are provided from different GC policies. Each example covers a particular type of cycle from a particular policy. By following the examples, you can learn how to interpret the XML elements in a log.

gencon examples

The gencon policy uses two types of cycle; a partial GC cycle and a global GC cycle. By default, the partial GC cycle runs a stop-the-world (STW) scavenge operation. On specific platforms, gencon can run a concurrent scavenge operation (-Xgc:concurrentScavenge) instead, if enabled at run time.

The start of a gencon cycle is recorded in the log by the following elements and attributes:

Table showing types of gencon cycle along with the corresponding trigger reason and XML elements for each type.
GC cycle Value of type attribute of the <cycle-start> and <cycle-end>elements Element that logs the cycle trigger Trigger reason
Global global <concurrent-kickoff> Low free memory tenure area threshold reached. Cycle trigger element is located before the <cycle-start> element.
Partial scavenge <af-start> Allocation failure. Cycle trigger element is located before the <cycle-start> element.

You can use the type attribute of the <gc-start> and <gc-end> elements to locate a particular cycle. You can also locate a particular type of cycle by searching for the element that records the cycle trigger, which is located before the <cycle-start> element.

You can analyze the increments and operations that are associated with a particular type of cycle by locating and interpreting the elements in the following table:

Table showing increments and operations that are associated with the gencon partial scavenge and global cycles.
GC process Elements that log the start and end of the event Details
GC cycle <cycle-start>, <cycle-end> The start and end of a GC cycle.
GC STW increment <gc-start>, <gc-end> The start and end of a GC increment that begins with a pause.
GC STW increment <concurrent-kickoff> The start of the initial GC increment of the global concurrent cycle that begins the initial mark operation.
GC STW increment <concurrent-global-final> The start of the final GC increment of the global concurrent cycle that executes the final collection.
GC operations and suboperations <gc-op> A GC operation such as mark or sweep, or a suboperation such as class unload.

Note: For more information about the XML structure of GC cycles, see GC cycles. For more information about GC cycle increments, see GC increments and interleaving.

The following examples use log excerpts to show how the different types of gencon cycle are logged.

Scavenge partial GC cycle

The following example is taken from a gencon log. The output is broken down into sections with supporting text to explain the GC processing that is taking place.

To search for a scavenge partial GC cycle, you can search for the type attribute value scavenge in cycle-start and cycle-end elements, or search for the <af> element that logs the allocation failure trigger.

By default, the gencon partial GC cycle runs by using a single STW pause. The cycle performs only one operation, a scavenge operation, which runs only on the nursery area. The cycle consists of a single GC increment, which is labeled by using the elements that are shown in the following table:

Table showing the gencon default partial scavenge cycle's GC increment and corresponding XML elements.
GC operation GC increment STW or concurrent XML element of GC increment Details
scavenge single STW <gc-start>, <gc-end> Contains detailed information about copied objects and the weak roots processing operation

The scavenge partial GC cycle follows a general structure in the verbose GC log as shown. Some elements are omitted for clarity:


<exclusive-start/>          (STW Pause starts)

<af-start/>                 (allocation failure trigger recorded)

<cycle-start/>              (scavenge cycle starts)

<gc-start>                  (scavenge cycle increment starts)

<mem-info>                  (memory status before operation)

<mem></mem>                 (status of different areas of heap)

</mem-info>

</gc-start>

<allocation-stats/>         (Snapshot of how memory was divided up between
                            ... application threads before current cycle started)

<gc-op> “scavenge"</gc-op>  (scavenge operation completed)

<gc-end>                    (scavenge cycle increment ends)

<mem-info>                  (memory status after operation)

<mem></mem>                 (status of different areas of heap)

</mem-info>

</gc-end>

</cycle-end>                (scavenge cycle ends)

<allocation-satisfied/>     (required allocation has been achieved)

<af-end/>

<exclusive-end>             (STW for scavenge cycle ends)
...

The first activity in the cycle is recorded by an <exclusive-start> element, which indicates the start of the STW pause. Application (or mutator) threads are halted to give the garbage collector exclusive access to the Java™ object heap:

<!-- Start of gencon scavenge partial GC cycle example -->

<exclusive-start id="12392" timestamp="2020-10-18T13:35:45.000" intervalms="406.180">
  <response-info timems="0.070" idlems="0.070" threads="0" lastid="00000000013D6900" lastname="LargeThreadPool-thread-68" />
</exclusive-start>

The <af-start> element indicates that the cycle was triggered by an allocation failure in the nursery (type="nursery") area of the heap:

<af-start id="12393" threadId="00000000013D7280" totalBytesRequested="8200" timestamp="2020-10-18T13:35:45.000" intervalms="418.233" type="nursery" />

The <cycle-start> element marks the start of the cycle. The attribute type="scavenge" confirms that this activity is a scavenge partial GC cycle:

<cycle-start id="12394" type="scavenge" contextid="0" timestamp="2020-10-18T13:35:45.000" intervalms="418.231" />

Most elements are labeled with an id attribute that increases in value incrementally, atimestamp attribute, and a contextid attribute. All elements that record GC increments and operations that are associated with a particular cycle have a contextid value that matches the id value of the cycle. The <cycle-start> element of this example cycle has an id="12394", so all subsequent elements that have a contextid="4", such as the <gc-start> increment element and the <gc-op> operation element, are associated with this particular example cycle.

The <gc-start> element records the first GC increment. In this <gc-start> section, you can find information about the amount of memory available (<mem-info>) and where it is located in the Java object heap.

The memory snapshot within the <gc-start> element is taken before the scavenge operation and can be compared with a similar snapshot that is taken afterward to understand the effect on the heap.

<gc-start id="12395" type="scavenge" contextid="12394" timestamp="2020-10-18T13:35:45.000">
  <mem-info id="12396" free="414960320" total="1073741824" percent="38">
    <mem type="nursery" free="0" total="268435456" percent="0">
      <mem type="allocate" free="0" total="241565696" percent="0" />
      <mem type="survivor" free="0" total="26869760" percent="0" />
    </mem>
    <mem type="tenure" free="414960320" total="805306368" percent="51">
      <mem type="soa" free="374694592" total="765040640" percent="48" />
      <mem type="loa" free="40265728" total="40265728" percent="100" />
    </mem>
    <remembered-set count="21474" />
  </mem-info>
</gc-start>

The following statements describe the object heap memory allocation at the start of the increment:

The next element <allocation-stats> shows a snapshot, which was taken before the cycle started, of how memory was divided up between application threads. In this example, the thread that used the most memory was LargeThreadPool-thread-79.

<allocation-stats totalBytes="235362176" >
  <allocated-bytes non-tlh="32880" tlh="235329296" />
  <largest-consumer threadName="LargeThreadPool-thread-79" threadId="00000000013F0C00" bytes="6288544" />
</allocation-stats>

The scavenge GC operation is recorded by the <gc-op> element; child elements record details about the operation. For example,

<gc-op id="12397" type="scavenge" timems="11.649" contextid="12394" timestamp="2020-10-18T13:35:45.012">
  <scavenger-info tenureage="7" tenuremask="4080" tiltratio="89" />
  <memory-copied type="nursery" objects="154910" bytes="6027440" bytesdiscarded="394832" />
  <memory-copied type="tenure" objects="16171" bytes="562848" bytesdiscarded="3064" />
  <ownableSynchronizers candidates="10838" cleared="10824" />
  <references type="soft" candidates="24" cleared="0" enqueued="0" dynamicThreshold="16" maxThreshold="32" />
  <references type="weak" candidates="390" cleared="269" enqueued="269" />
  <references type="phantom" candidates="1" cleared="0" enqueued="0" />
  <object-monitors candidates="132" cleared="0"  />
</gc-op>

The <memory-copied> element indicates that 5.75 MB (6,027,440B) of reachable objects were moved by the scavenge operation from the allocate space to the survivor space in the nursery area, and 0.54 MB(562,848 B) were moved to the tenure area.

The <scavenger-info> element shows that the tenure age is set to 7. Any object in the allocate space with an age less than or equal to 7 is copied to the survivor space during this scavengeoperation. Any object that is copied between the allocate and survivor areas more than 7 times is moved to the tenure area.

For more information about how the scavenge operation acts on the Java object heap, see GC processing.

The end of the increment is recorded with <gc-end> and provides another snapshot of memory allocation on the heap, similar to <gc-start>.

<gc-end id="12398" type="scavenge" contextid="12394" durationms="11.785" usertimems="46.278" systemtimems="0.036" stalltimems="0.145" timestamp="2020-10-18T13:35:45.012" activeThreads="4">
  <mem-info id="12399" free="649473560" total="1073741824" percent="60">
    <mem type="nursery" free="235142120" total="268435456" percent="87">
      <mem type="allocate" free="235142120" total="241565696" percent="97" />
      <mem type="survivor" free="0" total="26869760" percent="0" />
    </mem>
    <mem type="tenure" free="414331440" total="805306368" percent="51" macro-fragmented="0">
      <mem type="soa" free="374065712" total="765040640" percent="48" />
      <mem type="loa" free="40265728" total="40265728" percent="100" />
    </mem>
    <pending-finalizers system="0" default="0" reference="269" classloader="0" />
    <remembered-set count="13792" />
  </mem-info>
</gc-end>

The Java object heap memory allocation at the end of the increment is as follows:

The scavenge operation successfully reclaimed memory in the allocate space of the nursery area by copying objects from the allocate space into the survivor space of the nursery area, and copying objects from the survivor space into the tenure area.

The cycle ends (<cycle-end>). The following <allocation-satisfied> element indicates that the allocation request that caused the allocation failure can now complete successfully. The STW pause ends with the <exclusive-end> element:

<cycle-end id="12400" type="scavenge" contextid="12394" timestamp="2020-10-18T13:35:45.012" />
<allocation-satisfied id="12401" threadId="00000000013D6900" bytesRequested="8200" />
<af-end id="12402" timestamp="2020-10-18T13:35:45.012" threadId="00000000013D7280" success="true" from="nursery"/>
<exclusive-end id="12403" timestamp="2020-10-18T13:35:45.012" durationms="12.319" />

<!-- End of gencon partial GC cycle example -->

Summary

Analyzing the structure and elements of this example log output shows that this example global cycle has the following characteristics:

Concurrent scavenge partial GC cycle (non-default)

When concurrent scavenge mode is enabled, the partial GC cycle is run as a Concurrent Scavenge cycle. This partial GC cycle is divided into increments to enable the majority of the scavenge operation to run concurrently with running application (or mutator) threads. The concurrent increment can run while application threads run, and also while the intermediate concurrent increment of the global GC cycle runs. The interleaving of the concurrent scavenge partial GC cycle with the global cycle can be seen in the logs.

The following elements log the GC increments and operations of the concurrent scavenge partial GC cycle:

Table showing the gencon concurrent (non-default) partial scavenge cycle's GC increment and corresponding XML elements.
GC operation GC increment STW or concurrent XML element of GC increment Details
scavenge initial STW <gc-start>, <gc-end> Root scanning, reported as a single scavenge operation.
scavenge intermediate concurrent <concurrent-start>, <concurrent-end> Live objects are traversed and evacuated (*copy forward*). Operation is reported as a scavenge operation.
scavenge final STW <gc-start>, <gc-end> weak roots scanning, reported as a complex scavenge operation. <gc-op> contains specific details for each of the weak root groups.

To search for a concurrent scavenge partial GC cycle, you can search for the type attribute value scavenge in cycle-start and cycle-end elements, or search for the <af> element that logs the allocation failure trigger.

You can locate the concurrent scavenge partial cycle's concurrent increment by searching for <concurrent-start> and <concurrent-end>. The global cycle's intermediate concurrent increment, which can run at the same time, is not logged by an element, but begins immediately after application threads are restarted following the <cycle-start type="global"/> element. For more information about the global cycle's intermediate concurrent increment, see gencon global GC cycle. For more information about GC increments, see GC increments and interleaving.

gencon global GC cycle

The following example shows how a global GC cycle is recorded in a gencon policy verbose GC log. The output is broken down into sections with supporting text to explain the GC processing that is taking place.

The global GC cycle runs when the tenure area is close to full, which typically occurs after many partial cycles. As such, the output can be found part way down a full log. For more information about the GC initialization section, see Initialization . For an example log output for a gencon partial cycle, see Scavenge partial GC cycle.

The global GC cycle is split into three increments, as shown in GC increments and interleaving. Splitting the cycle operations into the following increments reduces pause times by running the majority of the GC work concurrently. The concurrent increment pauses when a partial GC cycle is triggered and resumes after the partial cycle, or multiple cycles, finish. The interleaving of partial GC cycles with the global cycle's intermediate concurrent increment can be seen in the following gencon global GC cycle log output. A single partial GC cycle is logged between the initial and final increments of the global cycle.

To search for a global cycle, you can search for the type attribute value global in cycle-start and cycle-end elements, or search for the element that logs the initial concurrent increment, <concurrent-kickoff>.

The following elements log the GC increments and operations of the global GC cycle:

Table showing the gencon global cycle's GC increment and corresponding XML elements.
GC operation GC increment STW or concurrent XML element of GC increment Details
n/a - initiates cycle initial STW <concurrent-kickoff> No <gc-op> is logged. This increment just initiates the concurrent mark increment.
concurrent mark intermediate concurrent none <concurrent-trace-info> records the progress of the concurrent mark increment.
final collection final STW <concurrent-global-final> The increment is typically triggered when a card cleaning threshold is reached. The completion of a tracing phase can also trigger the increment. Operations include a final concurrent mark, a sweep, and an optional class unload and compact.

The global GC cycle follows a general structure in the verbose GC log. Some child elements are omitted for clarity. Multiple partial GC cycles can start and finish between the start and end of a global GC cycle. In the following example, the structure includes a single partial GC cycle within the global cycle:


<concurrent-kickoff/>       (global cycle 1st increment recorded)

<exclusive-start/>          (STW pause starts)

<cycle-start/>              (global cycle starts)

<exclusive-end/>            (STW pause ends)

(mutator threads running, global cycle concurrent increment running concurrently)


<exclusive-start/>          (STW for partial GC cycle starts)


...                         (partial GC cycle starts and completes)


<exclusive-end/>            (STW for partial GC cycle ends)

(mutator threads running, global cycle concurrent increment running concurrently)

<exclusive-start/>          (STW pause starts)

<concurrent-global-final/>  (global cycle final increment recorded)

<gc-start/>                 (global cycle final increment starts)

<allocation-stats/>         (Snapshot of how memory was divided up between
                            ... application threads before current cycle started)

<mem-info>                  (memory status before operations)

<mem></mem>                 (status of different areas of heap)

</mem-info>

</gc-start>

<gc-op> “type=rs-scan"</gc-op>        (remembered set scan completed)

<gc-op>”type=card-cleaning" </gc-op>  (card cleaning completed)

<gc-op> “type=mark”</gc-op>           (final mark operation and weak roots processing completed)

<gc-op> “type=classunload”</gc-op>    (class unload operation completed)

<gc-op ”type=sweep” />                (sweep operation completed)

<gc-end>                     (global cycle final increment ends)

<mem-info>                   (memory status after operations)

<mem></mem>                  (status of different areas of heap)

</mem-info>

</gc-end>

</cycle-end>                 (global cycle ends)

<exclusive-end>              (STW pause ends)

<exclusive-start>            (STW pause starts)
...

The first activity in the cycle is recorded by a <concurrent-kickoff> element, which records the start of the first of three increments that make up a gencon global GC cycle. The <concurrent-kickoff> element records the following information:

<concurrent-kickoff id="12362" timestamp="2020-10-18T13:35:44.341">

<kickoff reason="threshold reached" targetBytes="239014924" thresholdFreeBytes="33024922" remainingFree="32933776" tenureFreeBytes="42439200" nurseryFreeBytes="32933776" />

</concurrent-kickoff>

For this example, the remainingFree bytes value of 31.4 MB (32,933,776B) is approaching the thresholdFreeBytes value of 31.5 MB (33,024,922B) so a global cycle is triggered.

This cycle aims to trace 228 MB (239,014,924B) during the concurrent increment. If the concurrent increment is interrupted by a card cleaning threshold value before it traces all 228 MB, the final STW increment completes the tracing during the STW pause.

Note: To analyze specific parts of a cycle, you can search for the elements that mark a specific increment of the cycle. For example, you can search for the element to locate the final increment of the gencon global cycle. See the details of a particular cycle, such as the gencon global GC cycle, to determine the element names for particular STW or concurrent GC increments or operations.

The next element recorded in the log, the <exclusive-start>element, records the start of an STW pause:

<exclusive-start id="12363" timestamp="2020-10-18T13:35:44.344" intervalms="342.152">

<response-info timems="0.135" idlems="0.068" threads="3" lastid="00000000015DE600" lastname="LargeThreadPool-thread-24" />

</exclusive-start>

The following <gc-start> element records details of the start of a new cycle.

<cycle-start id="12364" type="global" contextid="0" timestamp="2020-10-18T13:35:44.344" intervalms="516655.052" />

The type attribute records the cycle as a global cycle. The contextid of the cycle is, which indicates that all GC events that are associated with this cycle are tagged in relation to the id of this cycle. In particular, all subsequent elements that are associated with this particular example cycle have a contextid value equal to the <cycle-start> id attribute value of “12634”.

The next element in the log is <exclusive-end>, which records the end of the STW pause:

<exclusive-end id="12365" timestamp="2020-10-18T13:35:44.344" durationms="0.048" />

The operations and suboperations of the second increment of the gencon global cycle are now running concurrently.

The next section of the logs records an STW pause that is associated with an allocation failure. The <cycle-start> element that follows this STW pause indicates that the cycle is a scavenge cycle, which is the partial GC cycle that is used by the gencon GC:

...
<cycle-start id="12368" type="scavenge" contextid="0" timestamp="2020-10-18T13:35:44.582" intervalms="580.047" />
...

Subsequent elements have a contextid=“12368”, which matches the id of this new scavengecycle. For more information about how this cycle is recorded in the logs, see Scavenge partial GC cycle.

The operations and suboperations of the second, concurrent increment of the gencon global cycle are paused while the STW scavenge operation is running, and resume when the STW pause finishes.

After the partial GC cycle completes and the STW pause finishes, the log records a new STW pause, which is triggered to enable the final gencon global GC increment to run. This final increment finishes marking the nursery area and completes the global cycle. The <exclusive-start> element is followed by a <concurrent-global-final> element, which logs the beginning of this final increment (and by implication, the end of the second increment).

<exclusive-start id="12378" timestamp="2020-10-18T13:35:44.594" intervalms="12.075">

<response-info timems="0.108" idlems="0.040" threads="3" lastid="00000000018D3800" lastname="LargeThreadPool-thread-33" />

</exclusive-start>

<concurrent-global-final id="12379" timestamp="2020-10-18T13:35:44.594" intervalms="516905.029" >

<concurrent-trace-info reason="card cleaning threshold reached" tracedByMutators="200087048" tracedByHelpers="12164180" cardsCleaned="4966" workStackOverflowCount="0" />

</concurrent-global-final>

The reason attribute of the <concurrent-trace-info> child element indicates that this final STW increment of the global cycle was triggered because a card-cleaning threshold was reached. The concurrent tracing was stopped prematurely and the targetBytes concurrent tracing target, recorded at the cycle start by <concurrent-kickoff>, was not achieved concurrently. If the concurrent tracing completes without interruption, the <concurrent-trace-info element logs reason=tracing completed.

In the next section that begins with the gc-start element, you can find information about the amount of memory available (<mem-info>) and where it is located in the java object heap. This snapshot is taken before the final increment's operations and suboperations are run and can be compared with a similar snapshot that is taken afterward to understand the effect on the heap. The child element attribute values of the<mem> and <mem-info> elements indicate the status of the memory.

Note: You can double check that the increment is associated with the GC global cycle in the example by checking the contextid attribute value matches the id=12364 attribute value of the cycle's element.

<gc-start id="12380" type="global" contextid="12364" timestamp="2020-10-18T13:35:44.594">

<mem-info id="12381" free="277048640" total="1073741824" percent="25">

<mem type="nursery" free="234609440" total="268435456" percent="87">

<mem type="allocate" free="234609440" total="241565696" percent="97" />

<mem type="survivor" free="0" total="26869760" percent="0" />

</mem>

<mem type="tenure" free="42439200" total="805306368" percent="5">

<mem type="soa" free="2173472" total="765040640" percent="0" />

<mem type="loa" free="40265728" total="40265728" percent="100" />

</mem>

<pending-finalizers system="0" default="0" reference="405" classloader="0" />

<remembered-set count="17388" />

</mem-info>

</gc-start>

<allocation-stats totalBytes="827488" >

<allocated-bytes non-tlh="96" tlh="827392" />

<largest-consumer threadName="LargeThreadPool-thread-68" threadId="00000000013D6900" bytes="65632" />

</allocation-stats>

The next element <allocation-stats> shows a snapshot of how memory was divided up between application threads before the current cycle started. In this example, the thread that used the most memory was LargeThreadPool-thread-68.

For this example, at the start of this GC increment, the tenure area is low on free memory, as expected. 25% of the total heap is available as free memory, which is split between the following areas of the heap:

The <gc-op> elements and their child elements contain information about the operations and suboperations in the increment. The final increment of the gencon global cycle consists of multiple operations, each logged with a <gc-op> element. The type of operation is shown by the <gc-op> type attribute. The final increment of the example log runs five types of operation:

  1. rs-scan
  2. card-cleaning
  3. mark
  4. classunload
  5. sweep

Note: The final increment of a gencon global cycle can include an optional compact suboperation.

For more information about the different types of GC operation, see GC operations.

<gc-op id="12382" type="rs-scan" timems="3.525" contextid="12364" timestamp="2020-10-18T13:35:44.598">

<scan objectsFound="11895" bytesTraced="5537600" workStackOverflowCount="0" />

</gc-op>

<gc-op id="12383" type="card-cleaning" timems="2.910" contextid="12364" timestamp="2020-10-18T13:35:44.601">

<card-cleaning cardsCleaned="3603" bytesTraced="5808348" workStackOverflowCount="0" />

</gc-op>

<gc-op id="12384" type="mark" timems="6.495" contextid="12364" timestamp="2020-10-18T13:35:44.607">

<trace-info objectcount="1936" scancount="1698" scanbytes="61200" />

<finalization candidates="389" enqueued="1" />

<ownableSynchronizers candidates="5076" cleared="523" />

<references type="soft" candidates="18420" cleared="0" enqueued="0" dynamicThreshold="32" maxThreshold="32" />

<references type="weak" candidates="19920" cleared="114" enqueued="60" />

<references type="phantom" candidates="671" cleared="50" enqueued="50" />

<stringconstants candidates="40956" cleared="109" />

<object-monitors candidates="182" cleared="51" />

</gc-op>

<gc-op id="12385" type="classunload" timems="1.607" contextid="12364" timestamp="2020-10-18T13:35:44.609">

<classunload-info classloadercandidates="425" classloadersunloaded="6" classesunloaded="2" anonymousclassesunloaded="1" quiescems="0.000" setupms="1.581" scanms="0.019" postms="0.007" />

</gc-op>

<gc-op id="12386" type="sweep" timems="9.464" contextid="12364" timestamp="2020-10-18T13:35:44.618" />

The end of the increment is recorded with <gc-end> and provides another snapshot of memory in the heap, similar to <gc-start>.

<gc-end id="12387" type="global" contextid="12364" durationms="24.220" usertimems="86.465" systemtimems="0.000" stalltimems="2.846" timestamp="2020-10-18T13:35:44.618" activeThreads="4">

<mem-info id="12388" free="650476504" total="1073741824" percent="60">

<mem type="nursery" free="235516088" total="268435456" percent="87">

<mem type="allocate" free="235516088" total="241565696" percent="97" />

<mem type="survivor" free="0" total="26869760" percent="0" />

</mem>

<mem type="tenure" free="414960416" total="805306368" percent="51" micro-fragmented="98245682" macro-fragmented="0">

<mem type="soa" free="374694688" total="765040640" percent="48" />

<mem type="loa" free="40265728" total="40265728" percent="100" />

</mem>

<pending-finalizers system="1" default="0" reference="515" classloader="0" />

<remembered-set count="13554" />

</mem-info>

</gc-end>

60% of the heap now contains free memory as a result of the final global cycle increment, which is split between the following areas of the heap:

Note: The global GC cycle runs to reclaim memory in the tenure area. The freeing up of memory in the nursery area is achieved by using the partial GC cycle. For more information, see gencon policy (default).

After the final increment of the global cycle completes, the global cycle ends and the STW pause ends, as shown in the following output:

<cycle-end id="12389" type="global" contextid="12364" timestamp="2020-10-18T13:35:44.619" />

<exclusive-end id="12391" timestamp="2020-10-18T13:35:44.619" durationms="24.679" />

Summary

Analyzing the structure and elements of this example log output shows that this example global cycle has the following characteristics:

balanced examples

The balanced policy (-Xgcpolicy:balanced) uses two types of cycle to perform GC; a partial GC cycle and a global GC mark cycle. The policy might also run a third type of cycle, which is a global cycle, to reclaim memory after an allocation failure that results from tight memory conditions.

For more information about the cycles used in a particular policy, see GC policies.

The start of a balanced cycle is recorded in the log by the following elements and attributes:

Table showing types of balanced cycle, the corresponding trigger, and XML elements for each `type`.
GC cycle or increment Value of type attribute of the cycle or increment elements Element that logs the cycle trigger Trigger reason
partial cycle partial gc <allocation-taxation> Allocation taxation threshold reached.
global mark cycle global mark phase <allocation-taxation> Allocation taxation threshold reached.
global mark STW subincrement of global mark cycle mark increment n/a Allocation taxation threshold reached
global mark concurrent subincrement of global mark cycle GMP work packet processing n/a Allocation taxation threshold reached
global cycle global garbage collect <af-start> (or <sys-start reason="explicit"> if triggered explicitly) Allocation failure. Occurs under tight memory conditions. Cycle runs rarely.

To locate a particular type of cycle, you can search for the type attribute of the <cycle-start> and <cycle-end> elements.

When memory in the Java object heap reaches a memory threshold, called an allocation taxation threshold, a balanced partial GC cycle, balanced global mark cycle, or balanced global mark cycle increment, is triggered. If the available memory in the heap is low, the GC triggers a balanced global mark cycle, or a global mark cycle increment if the global mark cycle is in progress. Otherwise, the GC triggers a partial cycle.

Partial GC cycles, global mark cycles, and global GC cycles set the allocation taxation threshold at the end of their cycle or increment to schedule the next cycle or increment. For balanced cycles, the taxation on the mutator threads refers to pausing the mutator threads while GC work is run.

When a partial cycle ends, if the cycle is not run between global mark phase increments of a global mark cycle, and a global mark cycle is not scheduled as the next cycle, the allocation taxation threshold is set to trigger the next partial cycle when the eden space is full. Specifically, the allocation threshold is set to be equal to the size of the eden space.

If a partial cycle runs within a global mark cycle, or if a global mark cycle is scheduled as the next cycle, the allocation taxation threshold, set at the end of the partial cycle, is set to be smaller than the size of the eden space. Specifically, the allocation taxation threshold is set to be half the size of the eden space so that the next global mark cycle or global mark cycle increment has enough memory available in the eden space to run.

For more information about GC increments, see GC increments and interleaving.

You can analyze the increments and operations that are associated with a particular type of cycle by locating and interpreting the elements in the following table:

Table showing increments and operations that are associated with the balanced partial and global mark cycles
GC process Elements that log the start and end of the event> Details
GC cycle <cycle-start>,<cycle-end> The start and end of a GC cycle
GC STW increment <gc-start><gc-end> The start and end of a GC increment or subincrement that begins with a STW pause. For example, a global mark phase global mark GC cycle increment or a partial GC cycle increment
GC concurrent increment <concurrent-start>, <concurrent-end> The start of the concurrent global mark phase work packet processing subincrements of the global mark cycle
GC operations and phases <gc-op> A GC operation such as mark or sweep, or a suboperation such as class unload.

For more information about the XML structure of GC cycles, see GC cycles.

The following sections use log excerpts to show how the different GC processes are logged.

balanced partial GC cycle

The following example is taken from a balanced policy verbose GC log. The output is broken down into sections to explain the GC processing that is taking place.

To search for a balanced partial GC cycle, you can search for the type attribute value partial gc in <cycle-start> and <cycle-end> elements.

The partial GC cycle reclaims memory in the heap for the allocation of new objects by reducing the number of used regions. The partial GC cycle always reduces used regions in the eden space and might also reclaim memory from older regions. Multiple partial GC cycles often run in between global mark phase increments of the balanced global mark GC cycle.

All the operations in a partial GC cycle run during a single STW pause, as shown in the following table:

Table showing the balanced partial GC cycle operation and corresponding XML elements.
GC operation GC increment STW or concurrent XML element of GC increment
copy forward, and optionally class unload, sweep, and compact single STW <gc-start>, <gc-end>

The following general structure shows a balanced partial GC cycle. Some child elements are omitted for clarity:

<exclusive-start/>                       (STW pause starts)

<allocation-taxation/>                   (memory threshold trigger recorded)

<cycle-start/>                            (partial cycle starts)

<gc-start/>                             (partial  cycle increment starts)

<mem-info>                            (memory status before operations)

  <mem></mem>                         (status of different types of memory)

</mem-info>         

</gc-start>

<allocation-stats/>                  (Snapshot of how memory was divided up between
                                     ... application threads before current cycle started)

<gc-op> type="copy forward" </gc-op>    (copy forward operation completed)

<gc-op> type="class unload" </gc-op>    (class unload operation completed)

<gc-op> type="sweep" </gc-op>           (sweep operation completed)

<gc-op> type="compact" </gc-op>         (compact operation completed)

<gc-end>                                (partial cycle increment ends)

<mem-info>                            (memory status after operations)

  <mem></mem>                         (status of different types of memory)

</mem-info>         

</gc-end>

<cycle-end>                               (partial cycle ends)

<exclusive-end>                           (STW pause ends)

When the balanced partial GC cycle is triggered, the GC runs an STW pause. Application (or mutator) threads are halted to give the garbage collector exclusive access to the heap. The STW pause is recorded in the logs by the <exclusive-start> element.

<exclusive-start id="184" timestamp="2021-02-26T11:11:42.310" intervalms="3745.790">
  <response-info timems="3.138" idlems="1.056" threads="2" lastid="00000000006EDE00" lastname="RunDataWriter.1" />
</exclusive-start>

An allocation taxation threshold triggers a balanced partial GC cycle. The logs record this trigger reason by using the<allocation-taxation> element.

<allocation-taxation id="185" taxation-threshold="2147483648" timestamp="2021-02-26T11:11:42.311" intervalms="3745.785" />

Details about the start of the cycle are recorded by the <cycle-start> element. The cycle is recorded as a partial gc with an id=336. Any subsequent elements that are associated with this cycle have a contextid=186 to match the cycle id. You can use this contextid value to distinguish the partial GC cycle increment and operations from interleaving increments and operations of other balanced cycles, such as global mark cycles.

<cycle-start id="186" type="partial gc" contextid="0" timestamp="2021-02-26T11:11:42.311" intervalms="3745.805" />

The partial cycle begins its only GC increment, recorded by using the <gc-start> element. You can understand the effect that the increment operations have on the heap by comparing snapshots of the memory that are taken at the start and the end of the increment. The child elements <mem-info> and <mem> of the <gc-start> and <gc-end> elements record the amount of memory available and where it is located in the heap.

<gc-start id="187" type="partial gc" contextid="186" timestamp="2021-02-26T11:11:42.311">
  <mem-info id="188" free="897581056" total="4294967296" percent="20">
    <mem type="eden" free="0" total="2147483648" percent="0" />
    <arraylet-primitive objects="1" leaves="4" largest="4" />
    <remembered-set count="2749664" freebytes="160705664" totalbytes="171704320" percent="93" regionsoverflowed="1" regionsstable="12" regionsrebuilding="0"/>
  </mem-info>
</gc-start>

As expected, at the start of this increment, the eden regions are full. 856 MB (897,581,056 B) of the total 4096 MB (4294,967,296 B) heap, equivalent to 20% of the heap, is available as free memory.

The status of the remembered set, a metastructure specific to Eclipse OpenJ9™ generational garbage collectors, is reported by the <remembered-set> element. The remembered set metastructure keeps a record of any object references that cross different regions. Each region corresponds to a single remembered set.

The partial GC cycle uses and prunes the remembered set. The regionsoverflowed value records the number of regions that exceeded the non-object heap memory allocation that is reserved for the remembered set. The partial GC cycle cannot reclaim memory from these overflow regions. The partial GC cycle also cannot reclaim memory from any regions whose remembered set is being rebuilt by an increment of a global mark cycle that is in progress.

At the start of the partial GC cycle, the remembered set is using 93% of its available memory capacity, with 153.26 MB (160705664 B) available. The set consists of 2,749,664 cards and has one overflow region.

The following element, <allocation-stats>, records information about how memory was divided between application (or mutator) threads before the start of the current cycle. For this example, the thread Group1.Backend.CompositeBackend{Tier1}.7 was the largest consumer of memory.

<allocation-stats totalBytes="2146431360" >
  <allocated-bytes non-tlh="96417448" tlh="2050013912" arrayletleaf="0"/>
  <largest-consumer threadName="Group1.Backend.CompositeBackend{Tier1}.7" threadId="00000000007E9300" bytes="275750048" />
</allocation-stats>

The operations of the GC increment are run and details are recorded in the <gc-op> elements. The logs show that this increment begins with a copy forward operation followed by a class unload. Other balanced partial GC cycles can also include sweep and compact operations. For more information about the operations involved in balanced partial GC cycles, see GC Processing.

<gc-op id="189" type="copy forward" timems="400.637" contextid="186" timestamp="2021-02-26T11:11:42.713">
  <memory-copied type="eden" objects="4434622" bytes="119281928" bytesdiscarded="1382272" />
  <memory-copied type="other" objects="8847813" bytes="244414264" bytesdiscarded="6243176" />
  <memory-cardclean objects="1446970" bytes="64143048" />
  <regions eden="512" other="80" />
  <remembered-set-cleared processed="2435794" cleared="887129" durationms="8.667" />
  <finalization candidates="66" enqueued="56" />
  <ownableSynchronizers candidates="256500" cleared="78012" />
  <references type="soft" candidates="153648" cleared="0" enqueued="0" dynamicThreshold="22" maxThreshold="32" />
  <references type="weak" candidates="1266" cleared="610" enqueued="430" />
  <stringconstants candidates="9479" cleared="0"  />
  <object-monitors candidates="13576" cleared="13505"  />
</gc-op>
<gc-op id="190" type="classunload" timems="0.010" contextid="186" timestamp="2021-02-26T11:11:42.713">
  <classunload-info classloadercandidates="179" classloadersunloaded="0" classesunloaded="0" anonymousclassesunloaded="0" quiescems="0.000" setupms="0.010" scanms="0.000" postms="0.000" />
</gc-op>

The logs show that the copy forward operation acts on the entire eden space (512 regions), recorded as type=eden, and 80 older regions, which are recorded as type=other. 113.76 MB (119281928 B) of memory was copied from the eden space to 1st generation regions and 233.10 MB (244414264 B) of memory in non-eden regions was copied to the next generation of regions. The copy forward operation is followed by a class unload operation.

In some cases, a copy forward operation moves some regions by copying forward the objects in those regions, but only marks the objects in other regions. For example, the following log excerpt is taken from a different partial cycle, which corresponds to a contextid of 2049. The copy forward operation in the following example involves marking some regions and copying forward other regions.

<gc-op id="2052" type="copy forward" timems="649.059" contextid="2049" timestamp="2021-02-26T11:22:34.901">
  <memory-copied type="eden" objects="95989" bytes="7882704" bytesdiscarded="501088" />
  <memory-copied type="other" objects="2955854" bytes="86854064" bytesdiscarded="626024" />
  <memory-cardclean objects="1304" bytes="56840" />
  <memory-traced type="eden" objects="23392785" bytes="553756840" />
  <memory-traced type="other" objects="5461302" bytes="131394216" />
  <regions eden="488" other="138" />
  <remembered-set-cleared processed="156775" cleared="4897" durationms="1.759" />
  <finalization candidates="31" enqueued="12" />
  <ownableSynchronizers candidates="1992467" cleared="1600904" />
  <references type="soft" candidates="329190" cleared="0" enqueued="0" dynamicThreshold="8" maxThreshold="32" />
  <references type="weak" candidates="697" cleared="105" enqueued="6" />
  <stringconstants candidates="9848" cleared="0"  />
  <object-monitors candidates="1437" cleared="1353"  />
  <heap-resize type="expand" space="default" amount="0" count="1" timems="0.000" reason="continue current collection" />
  <warning details="operation aborted due to insufficient free space" />
</gc-op>

The logs record these two concurrent parts of a copy forward operation in the <gc-op type="copy forward"> section by using a <memory-traced> child element. In addition,evacuated and marked attributes for the <regions> child element are used to distinguish between the number of regions that were copied-forward (recorded as evacuated) and the number of regions that were only marked and not copied-forward. For example, <regions eden="256" other="308" evacuated="308" marked="256" />.

Returning to the contextid=186 partial cycle example, the next element in the logs, <gc-end>, records the end of the increment and provides another snapshot of memory allocation on the heap, similar to <gc-start>.

<gc-end id="191" type="partial gc" contextid="186" durationms="402.645" usertimems="3157.520" systemtimems="4.000" stalltimems="47.689" timestamp="2021-02-26T11:11:42.714" activeThreads="8">
  <mem-info id="192" free="3003121664" total="4294967296" percent="69">
    <mem type="eden" free="2147483648" total="2147483648" percent="100" />
    <arraylet-primitive objects="1" leaves="4" largest="4" />
    <pending-finalizers system="56" default="0" reference="430" classloader="0" />
    <remembered-set count="2922048" freebytes="160016128" totalbytes="171704320" percent="93" regionsoverflowed="1" regionsstable="12" regionsrebuilding="0"/>
  </mem-info>
</gc-end>

The following information describes the heap memory allocation at the end of the increment:

The cycle completes and the GC restarts application threads.

<cycle-end id="193" type="partial gc" contextid="186" timestamp="2021-02-26T11:11:42.714" />
<exclusive-end id="194" timestamp="2021-02-26T11:11:42.714" durationms="404.145" />

The next cycle that is recorded in the logs is another partial GC cycle. The <gc-start>element records the following information:

<gc-start id="198" type="partial gc" contextid="197" timestamp="2021-02-26T11:11:46.072">
  <mem-info id="199" free="855638016" total="4294967296" percent="19">
    <mem type="eden" free="0" total="2147483648" percent="0" />
    <arraylet-primitive objects="1" leaves="4" largest="4" />
    <remembered-set count="2922048" freebytes="160016128" totalbytes="171704320" percent="93" regionsoverflowed="1" regionsstable="12" regionsrebuilding="0"/>
  </mem-info>
</gc-start>

The <mem-info> element shows that the following events occurred in between the end of the last (partial) GC cycle and the start of this cycle:

Summary

Analyzing the structure and elements of this example log output shows that this example balanced partial GC cycle has the following characteristics:

balanced global mark GC cycle

The global mark GC cycle uses a mixture of STW and concurrent operations to build a new record of object liveness across the heap for use by the balanced partial GC cycle. The balanced GC runs a balanced global mark cycle, or a balanced global mark cycle increment if the global mark cycle is in progress, if the heap satisfies a low memory condition when the allocation taxation threshold is reached.

The global mark cycle runs a global mark phase and also triggers an associated sweep phase within the partial GC cycle that immediately follows the end of the global mark cycle.

To search for a balanced global mark cycle, you can search for the type attribute value global mark phase in <cycle-start> and <cycle-end> elements.

The global cycle is split into multiple increments, each recorded as type="global mark phase". A global mark phase increment involves an STW subincrement, which runs a global mark operation during an STW pause, followed by a global mark phase (GMP) work packet subincrement. The GMP work packet subincrement involves a processing operation that runs concurrently. The GMP work packet subincrement might also use an STW pause to complete if the subincrement is interrupted by a partial or global cycle trigger.

Splitting the global mark phase into these increments and subincrements reduces pause times by running the majority of the GC work concurrently and interleaving global mark phase increments with partial GC cycles, and, rarely a balanced global GC cycles.

The following elements log the GC increments, subincrements, and operations of the global mark GC cycle:

Table showing the global mark cycle GC increments and corresponding XML elements
GC increment GC operations> STW or concurrent XML element of GC increment Details
global mark phase subincrement mark STW <gc-start>, <gc-end> The global mark phase operations start at the beginning of the cycle and run through all regions until the final region
GMP work packet processing subincrement Work packet processing (WPP) operations concurrent and sometimes final operations during an STW to complete the subincrement <concurrent-start>, <concurrent-end> The GMP work packet processing subincrement runs immediately after the global mark phase
final global mark phase increment final global mark phase operations including class unload STW <gc-start>, <gc-end> Final increment. Runs the final global mark phase operations, including weak roots processing, followed by operations to finish the cycle

The following structure shows a balanced global mark GC cycle. The lines are indented to help illustrate the flow and some child elements are omitted for clarity:

<exclusive-start/>                        (STW pause starts)

<allocation-taxation/>                    (memory threshold trigger recorded)

<cycle-start type="global mark phase"/>   (global mark cycle starts)

<gc-start type="global mark phase"/>      (1st GMP STW subincrement starts)

    <mem-info>                            (memory status before operations)

    <remembered-set>

    </mem-info>         

</gc-start>

<gc-op type="mark increment" />           (STW copy forward operation completed)

<gc-end>                                  (1st GMP STW subincrement ends)

    <mem-info>                            (memory status after operations)

    <remembered-set>

    </mem-info>

<gc-end>

<concurrent-start type="GMP work packet processing"/> (1st GMP concurrent subincrement starts)

<exclusive-end/>                                      (STW pause ends and application threads resume)

<concurrent-end type="GMP work packet processing"/>   (1st GMP concurrent subincrement ends)

<gc-op type="mark increment"/>                        (marking operation runs concurrently)

</concurrent-end type="GMP work packet processing"/>

...                                       (application threads run. STW pauses stop    
                                          and start application threads to run
                                          partial GC cycles.)

<exclusive-start/>                        (STW pause starts)

<gc-start type="global mark phase"/>      (2nd STW GMP subincrement starts)

...   

<concurrent-start type="GMP work packet processing"/> (2nd concurrent GMP subincrement starts)

...

<exclusive-end/>

...                                       (application threads run. Partial GC cycles may run)

<concurrent-end type="GMP work packet processing" />  (2nd concurrent GMP subincrement ends)
...

</concurrent-end>

...                                       (application threads run. Partial cycles
                                          and GMP increments interleave)

<exclusive-start/>                        (STW pause starts)

...

<gc-start type="global mark phase"/>      (final STW GMP subincrement starts.)

<gc-op type="mark increment" />           (STW copy forward operation completed)

<gc-op  type="class unload" />            (STW class unload operation completed)

<gc-end>                                  (1st GMP STW subincrement ends)
...   
<gc-end type="global mark phase"/>        (final STW GMP subincrement ends. No concurrent subincrement runs)

<cycle-end type="global mark phase"/>     (end of global mark cycle)

<exclusive-end/>                          (STW pause ends)    

<exclusive-start/>                        (STW pause starts)

<cycle-start type="partial gc" />         (partial cycle starts)

...

<gc-op type="sweep" />                    (Sweep operation associated with global mark cycle runs)

...

<cycle-end type="partial gc"/>            (partial GC cycle ends)

<exclusive-end/>                          (STw pause ends)

Global mark phase

The first activity of the global mark cycle is an STW pause, recorded by an <exclusive-start> element that precedes the <cycle-start type="global mark phase"/> element. The garbage collector pauses application threads to run the initial operations.

<exclusive-start id="1152" timestamp="2021-02-26T11:17:25.033" intervalms="1931.263">
  <response-info timems="3.082" idlems="1.041" threads="2" lastid="00000000006EDE00" lastname="RunDataWriter.1" />
</exclusive-start>

The <allocation-taxation> element indicates that an allocation taxation threshold triggered the cycle. The taxation threshold is recorded as 1024 MB (1,073,741,824), which is half the total memory of the eden space (2048 MB), as expected for threshold triggers of global mark cycles and increments. For more information about taxation thresholds for the balanced policy, see balanced examples.

<allocation-taxation id="1153" taxation-threshold="1073741824" timestamp="2021-02-26T11:17:25.034" intervalms="1931.251" />

Details about the start of the global mark GC cycle are recorded by the <cycle-start> element. The cycle is recorded as type global mark phase with id=1154. Any subsequent elements that are associated with this cycle have a contextid=1154 to match the global mark GC cycle id. You can use the contextid value to distinguish increments and operations of the global mark GC cycle from the partial cycles that interleave with it.

<cycle-start id="1154" type="global mark phase" contextid="0" timestamp="2021-02-26T11:17:25.034" intervalms="374365.075" />

The cycle begins with the STW subincrement of a global mark phase increment. The STW subincrement is recorded by using the <gc-start> element of type global mark phase.

<gc-start id="1155" type="global mark phase" contextid="1154" timestamp="2021-02-26T11:17:25.034">
  <mem-info id="1156" free="1442840576" total="4294967296" percent="33">
    <remembered-set count="2197888" freebytes="162912768" totalbytes="171704320" percent="94" regionsoverflowed="3" regionsstable="130" regionsrebuilding="0"/>
  </mem-info>
</gc-start>

The <gc-start> element provides a snapshot of the free memory available in the heap and the status of the remembered set. At the start of the increment, the heap is 33% free; 1376 MB (1442840576 B) of the total 4096 MB (4294967296 B).

The <remembered-set> element records the status of the remembered set metastructure, a structure that records object references that cross different regions. During the rebuilding of the remembered set metastructure, any regions that cannot be rebuilt into a remembered set due to a lack of memory resource in the metastructure are marked as overflow regions. Partial GC cycles cannot reclaim memory from overflow regions.

The aim of the global mark cycle is to create a new record of object liveness by populating the remembered set. The global mark cycle also attempts to rebuild the remembered set information for the overflowed regions, which can be seen in the remembered set statistics. After the global mark cycle completes, the remembered set reflects a closer snapshot of the current liveness of the heap. This more accurate snapshot of object liveness optimizes the pruning of the set, which is run by the partial GC cycle when it consumes the object liveness snapshot.

The logs show that at the start of this STW subincrement, the remembered set count is 2,197,888 cards, the metastructure is using 94% of its total available memory, and three overflow regions need to be rebuilt.

The <gc-op> element records that the STW subincrement runs a mark operation. This operation begins the process of building a record of object liveness across the heap.

<gc-op id="1157" type="mark increment" timems="122.825" contextid="1154" timestamp="2021-02-26T11:17:25.157">
  <trace-info objectcount="7726701" scancount="7584109" scanbytes="213445656" />
</gc-op>

The <trace-info> element records information about the marking and scanning stages of the mark increment operation. objectcount records the number of objects that were marked, ready for tracing. After marking live objects, a scan is run to trace objects and references. The following values are recorded:

In the example, the mark increment operation marked 7,726,701 objects and scanned 7,584,109 of these marked objects. The 7,584,109 of scanned objects take up 203.5 MB (213445656 B) of memory. The number of scanned objects is less than the number of marked objects because only objects that have children require scanning. Also, the scanning part of the marking operation might be interrupted by the garbage collector if a trigger threshold for a partial cycle or global cycle is reached during the marking operation.

The STW global mark phase subincrement ends, as recorded by <gc-end>, which records a snapshot of the memory status in the heap in a similar way to <gc-start>.

<gc-end id="1158" type="global mark phase" contextid="1154" durationms="123.139" usertimems="977.851" systemtimems="0.000" stalltimems="1.453" timestamp="2021-02-26T11:17:25.157" activeThreads="8">
  <mem-info id="1159" free="1442840576" total="4294967296" percent="33">
    <remembered-set count="3263968" freebytes="158648448" totalbytes="171704320" percent="92" regionsoverflowed="0" regionsstable="0" regionsrebuilding="133"/>
  </mem-info>
</gc-end>

The following comparison can be made between the snapshot at the beginning and end of this STW global mark phase subincrement:

The beginning of the second part of the global mark phase increment, the GMP work packet processing subincrememt, is recorded by <concurrent-start>. The child element <concurrent-mark-start> records the scan target of this subincrement as 242.74 MB (254,532,672 B).

<concurrent-start id="1160" type="GMP work packet processing" contextid="1154" timestamp="2021-02-26T11:17:25.157">
  <concurrent-mark-start scanTarget="254532672" />
</concurrent-start>

Now that the STW global mark phase subincrement is complete, application threads are restarted.

<exclusive-end id="1161" timestamp="2021-02-26T11:17:25.157" durationms="123.936" />

The GMP work packet processing subincrement continues to run concurrently. The end of this operation is recorded by using the <concurrent-end> element.

<concurrent-end id="1162" type="GMP work packet processing" contextid="1154" timestamp="2021-02-26T11:17:25.469" terminationReason="Work target met">
<gc-op id="1163" type="mark increment" timems="311.867" contextid="1154" timestamp="2021-02-26T11:17:25.469">
  <trace-info scanbytes="254708852" />
</gc-op>
</concurrent-end>

The child element <trace-info> shows that the processing scanned 242.91 MB (254,708,852 B), which slightly exceeds the 108.25 MB scan target.

Application threads continue to run and allocate memory. The garbage collector stops and starts the application threads to run partial GC cycles that reclaim free space in the eden space and some older regions. To see an example of how a balanced partial GC cycle appears in the logs, see the balanced partial GC cycle.

Following some partial GC cycles, an allocation taxation threshold is reached that triggers an STW pause followed by another global mark phase increment. The element <gc-start> in the following log excerpt has a contextid=1154 and type global mark phase, which indicates that this is a global mark phase subincrement associated with the global mark cycle example.

<exclusive-start id="1175" timestamp="2021-02-26T11:17:28.993" intervalms="1978.886">
  <response-info timems="5.111" idlems="1.714" threads="2" lastid="00000000006EDE00" lastname="RunDataWriter.1" />
</exclusive-start>
<allocation-taxation id="1176" taxation-threshold="1073741824" timestamp="2021-02-26T11:17:28.994" intervalms="1978.879" />
<gc-start id="1177" type="global mark phase" contextid="1154" timestamp="2021-02-26T11:17:28.994">
  <mem-info id="1178" free="1451229184" total="4294967296" percent="33">
    <remembered-set count="3325824" freebytes="158401024" totalbytes="171704320" percent="92" regionsoverflowed="2" regionsstable="0" regionsrebuilding="133"/>
  </mem-info>
</gc-start>

The <allocation-taxation> element shows that the allocation taxation threshold, which triggers this global mark phase increment, is set to 1024 MB, half of the size of the eden space, as expected.

<gc-start> records that the heap has 1384 MB (1,451,229,184 B) of free memory available at the beginning of this global mark phase increment. This value compares to the 1376 MB (1,442,840,576 B) of free memory available at the end of the previous global mark phase increment. Although free memory was reclaimed by the partial GC cycles that ran between these global mark phase increments, free memory was allocated to objects when application threads ran, resulting in a net reduction of free memory available.

The status of the heap at the beginning and end of STW subincrements are automatically recorded. For this STW subincrement, there are no <gc-op> elements recorded; <gc-end> immediately follows <gc-start> in the logs. For some STW subincrements, a mark operation is run.

<gc-end id="1179" type="global mark phase" contextid="1154" durationms="0.289" usertimems="1.000" systemtimems="0.000" stalltimems="0.000" timestamp="2021-02-26T11:17:28.994" activeThreads="8">
  <mem-info id="1180" free="1451229184" total="4294967296" percent="33">
    <remembered-set count="3325824" freebytes="158401024" totalbytes="171704320" percent="92" regionsoverflowed="2" regionsstable="0" regionsrebuilding="133"/>
  </mem-info>
</gc-end>

The second part of the increment, the GMP work packet processing subincrement, is recorded by using the <concurrent-start> and <concurrent-end> elements.

<concurrent-start id="1181" type="GMP work packet processing" contextid="1154" timestamp="2021-02-26T11:17:28.994">
  <concurrent-mark-start scanTarget="258671414" />
</concurrent-start>
<exclusive-end id="1182" timestamp="2021-02-26T11:17:28.994" durationms="0.816" />

<concurrent-end id="1183" type="GMP work packet processing" contextid="1154" timestamp="2021-02-26T11:17:29.273" terminationReason="Work target met">
<gc-op id="1184" type="mark increment" timems="279.311" contextid="1154" timestamp="2021-02-26T11:17:29.274">
  <trace-info scanbytes="258767612" />
</gc-op>
</concurrent-end>

The log excerpt shows the concurrent GMP work packet processing subincrement achieved the scan target of 246.69 MB (258671414 B). 246.78 MB (258767612 B) were scanned.

More partial cycles run. This pattern of interleaving of global mark increments with partial GC cycles repeats until a final global mark increment completes the global mark cycle. The final global mark phase increment consists of an STW global mark phase subincrement that includes mark increment and class unload operations.

<exclusive-start id="1217" timestamp="2021-02-26T11:17:36.864" intervalms="1986.124">
  <response-info timems="0.287" idlems="0.104" threads="2" lastid="00000000006EDE00" lastname="RunDataWriter.1" />
</exclusive-start>
<allocation-taxation id="1218" taxation-threshold="1073741824" timestamp="2021-02-26T11:17:36.865" intervalms="1986.101" />
<gc-start id="1219" type="global mark phase" contextid="1154" timestamp="2021-02-26T11:17:36.865">
  <mem-info id="1220" free="1438646272" total="4294967296" percent="33">
    <remembered-set count="3514496" freebytes="157646336" totalbytes="171704320" percent="91" regionsoverflowed="3" regionsstable="0" regionsrebuilding="133"/>
  </mem-info>
</gc-start>
<gc-op id="1221" type="mark increment" timems="164.843" contextid="1154" timestamp="2021-02-26T11:17:37.030">
  <trace-info objectcount="7715572" scancount="7665293" scanbytes="214739196" />
  <cardclean-info objects="3962203" bytes="117924792" />
  <finalization candidates="206" enqueued="30" />
  <ownableSynchronizers candidates="601780" cleared="16925" />
  <references type="soft" candidates="718240" cleared="2858" enqueued="2832" dynamicThreshold="18" maxThreshold="32" />
  <references type="weak" candidates="2321" cleared="142" enqueued="0" />
  <references type="phantom" candidates="8" cleared="0" enqueued="0" />
  <stringconstants candidates="9522" cleared="0"  />
  <object-monitors candidates="7142" cleared="7066"  />
</gc-op>
<gc-op id="1222" type="classunload" timems="0.704" contextid="1154" timestamp="2021-02-26T11:17:37.030">
  <classunload-info classloadercandidates="185" classloadersunloaded="13" classesunloaded="13" anonymousclassesunloaded="0" quiescems="0.000" setupms="0.644" scanms="0.043" postms="0.016" />
</gc-op>
<gc-end id="1223" type="global mark phase" contextid="1154" durationms="169.521" usertimems="1244.810" systemtimems="3.000" stalltimems="27.792" timestamp="2021-02-26T11:17:37.034" activeThreads="8">
  <mem-info id="1224" free="1438646272" total="4294967296" percent="33">
    <pending-finalizers system="30" default="0" reference="2832" classloader="0" />
    <remembered-set count="2241440" freebytes="162738560" totalbytes="171704320" percent="94" regionsoverflowed="3" regionsstable="127" regionsrebuilding="0"/>
  </mem-info>
</gc-end>

Comparing the memory at the start and end of this final global mark phase increment shows the following status:

Following the final global mark increment, the global mark cycle completes and the GC ends the STW pause.

<cycle-end id="1225" type="global mark phase" contextid="1154" timestamp="2021-02-26T11:17:37.034" />
<exclusive-end id="1226" timestamp="2021-02-26T11:17:37.034" durationms="170.186" />

The operations to create a record of object liveness across the heap, which began with the global mark cycle, is followed by a sweep phase. The sweep phase is triggered by the end of the global mark cycle to be included in the next partial GC cycle that runs.

Sweep phase

The sweep operation has the following two objectives:

While the global sweep operation is logically associated with the global mark phase, it does not run in the same global mark cycle. Instead, the sweep operation runs in the same STW increment as the first partial GC cycle that runs after the completion of the global mark cycle. This can be seen in the following log excerpt. After the log records the end of the global mark cycle, it records an STW pause followed by a partial gc cycle of id=1229. The global sweep operation that runs after the global mark phase is recorded in the <gc-op> element that is tagged as id=1229.

<exclusive-start id="1227" timestamp="2021-02-26T11:17:38.804" intervalms="1940.125">
...
<cycle-start id="1229" type="partial gc" contextid="0" timestamp="2021-02-26T11:17:38.805" intervalms="3926.202" />
...
</gc-start>
...
</gc-start>
<gc-op id="1232" type="sweep" timems="9.472" contextid="1229" timestamp="2021-02-26T11:17:38.815" />
<gc-op id="1233" type="copy forward" timems="308.258" contextid="1229" timestamp="2021-02-26T11:17:39.124">
...
<gc-op id="1234" type="classunload" timems="0.012" contextid="1229" timestamp="2021-02-26T11:17:39.125">
...
<gc-end>
...
</gc-end>
<cycle-end id="1237" type="partial gc" contextid="1229" timestamp="2021-02-26T11:17:39.125" />
<exclusive-end id="1238" timestamp="2021-02-26T11:17:39.125" durationms="320.792" />

A record of object liveness is now complete.

Summary

Analyzing the structure and elements of this example log output shows that this example balanced global mark GC cycle has the following characteristics:

balanced global GC cycle

The following global GC cycle example is taken from a balanced verbose GC log. The output is broken down into sections to explain the GC processing that is taking place.

A balanced global cycle is triggered if the VM is close to throwing an out of memory exception. This situation occurs only under tight memory conditions when the garbage collector cannot reclaim enough memory by using only partial and global mark cycles.

To search for a balanced global cycle or increment, you can search for the type attribute value global garbage collect of the cycle or increment element.

If the balanced global cycle is triggered during a balanced global mark GC cycle, a new global cycle is not recorded. Instead, the global mark cycle's global mark phase increment switches to a global garbage collect increment that is run as an STW increment. This switch is recorded in the logs by using a <cycle-continue> element, which precedes the gc-start element that records the new global garbage collect increment.

If the balanced global cycle is not triggered during a balanced global mark cycle, the global cycle is recorded as a new cycle by using the <cycle-start> element.

The element <sys-start reason="explicit"> is used in the logs to record a cycle that was triggered explicitly rather than by the garbage collector. For example, the trigger reason is recorded as explicit if a cycle is triggered by an application calling System.gc(). For more information about explicitly or implicitly triggering a GC cycle, see Garbage collection.

The global cycle operations run as a single GC increment during an STW pause.

Table showing the balanced global cycle's GC increment and corresponding XML elements.
GC increment GC operations STW or concurrent XML element of GC increment Details
single STW mark-sweep operations, optionally followed by a compact operation STW <cycle-start>, <gc-end> Contains detailed information about where free memory is located and remembered set statistics

If the global cycle is triggered during a global mark cycle, the global cycle follows a general structure in the verbose GC log as shown. Some child elements are omitted for clarity:

...                                        (global mark cycle increment runs)

<af-start/>                                (allocation failure trigger recorded)

<concurrent-end/>                          (global mark cycle concurrent subincrement finishes )

<allocation-taxation/>                     (memory threshold trigger recorded)

<cycle-continue/>                          (change of cycle type from global mark to global)

</gc-start type="global garbage collect"/> (global cycle STW increment starts)

<mem-info>                                 (memory status before operations)

 <mem></mem>                               (status of different types of memory)

</mem-info>         

</gc-start type="global garbage collect"/>

<allocation-stats/>                        (Snapshot of how memory was divided up between
                                           ... application threads before current cycle started)
<gc-op> type="mark" </gc-op>                (mark operation completed)

<gc-op> type="class unload" </gc-op>        (class unload operation completed)

<gc-op> type="sweep" </gc-op>               (sweep operation completed)

<gc-op> type="compact" </gc-op>             (compact operation completed)

<gc-end type="global garbage collect">      (global cycle STW increment ends)

<mem-info>                                  (memory status after operations)

<mem></mem>                                 (status of different types of memory)

</mem-info>         

</gc-end type="global garbage collect">

<cycle-end type = "global garbage collect"/> (cycle ends)

<allocation-satisfed/>                      (required allocation has been achieved)

<exclusive-end>                             (STW pause ends)

The following example shows a balanced global cycle that is triggered during a global mark cycle.

The start of the GMP work processing subincrement of the global mark cycle, which runs concurrently with application threads, is recorded by using the <concurrent-start> element.

<concurrent-start id="2009" type="GMP work packet processing" contextid="2003" timestamp="2021-03-05T12:16:43.109">
  <concurrent-mark-start scanTarget="18446744073709551615" />
</concurrent-start>

After the start of the concurrent subincrement, the logs record an allocation failure by using <af-start>. The <concurrent-end> element attribute terminationReason shows that a termination of the concurrent increment was requested by the garbage collector.

<af-start id="2010" threadId="00000000008AA780" totalBytesRequested="24" timestamp="2021-03-05T12:16:43.109" intervalms="1212.727" />
<concurrent-end id="2011" type="GMP work packet processing" contextid="2003" timestamp="2021-03-05T12:16:43.110" terminationReason="Termination requested">
<gc-op id="2012" type="mark increment" timems="0.893" contextid="2003" timestamp="2021-03-05T12:16:43.110">
  <trace-info scanbytes="584612" />
</gc-op>
</concurrent-end>

The next element, the <cycle-continue> element, records information about the switch of cycle type from a global mark cycle, recorded as type global mark phase, to a global cycle, recorded as type global garbage collect.

<cycle-continue id="2013" oldtype="global mark phase" newtype="global garbage collect" contextid="2003" timestamp="2021-03-05T12:16:43.110" />

A global cycle increment is recorded by <gc-start> and has the same contextid as the global mark cycle's elements. The global cycle operations are run during an STW pause and as a modification to the global mark cycle rather than a new cycle. The memory snapshot within the <gc-start> element is taken before the global increment's operations run and can be compared with a similar snapshot that is taken afterward to understand the effect on the heap.

<gc-start id="2014" type="global garbage collect" contextid="2003" timestamp="2021-03-05T12:16:43.110">
  <mem-info id="2015" free="0" total="838860800" percent="0">
    <mem type="eden" free="0" total="524288" percent="0" />
    <remembered-set count="12832" freebytes="33331072" totalbytes="33382400" percent="99" regionsoverflowed="0" regionsstable="0" regionsrebuilding="1593"/>
  </mem-info>
</gc-start>

At the start of the global cycle's increment, the amount of memory available in the heap is zero. In some cases, the heap is close to full, and in other cases, the memory is full.

The next element <allocation-stats> shows a snapshot of how memory was divided up between application threads before the current cycle started.

<allocation-stats totalBytes="524200" >
  <allocated-bytes non-tlh="0" tlh="524200" arrayletleaf="0"/>
</allocation-stats>

The <allocation-stats> element shows that very little allocation took place. Global cycles are triggered due to an allocation failure, so the low memory allocation values are expected.

The following operations, each recorded by a <gc-op> element, run as part of the global cycle's increment:

<gc-op id="2016" type="global mark" timems="357.859" contextid="2003" timestamp="2021-03-05T12:16:43.468">
  <trace-info objectcount="37461962" scancount="37447916" scanbytes="828311396" />
  <cardclean-info objects="311" bytes="22632" />
  <finalization candidates="195" enqueued="2" />
  <ownableSynchronizers candidates="2089" cleared="0" />
  <references type="soft" candidates="3059" cleared="0" enqueued="0" dynamicThreshold="0" maxThreshold="32" />
  <references type="weak" candidates="10797" cleared="0" enqueued="0" />
  <references type="phantom" candidates="6" cleared="0" enqueued="0" />
  <stringconstants candidates="10031" cleared="0"  />
</gc-op>
<gc-op id="2017" type="classunload" timems="0.123" contextid="2003" timestamp="2021-03-05T12:16:43.468">
  <classunload-info classloadercandidates="25" classloadersunloaded="0" classesunloaded="0" anonymousclassesunloaded="0" quiescems="0.000" setupms="0.123" scanms="0.000" postms="0.000" />
</gc-op>
<gc-op id="2018" type="sweep" timems="5.120" contextid="2003" timestamp="2021-03-05T12:16:43.474" />
<gc-op id="2019" type="compact" timems="762.323" contextid="2003" timestamp="2021-03-05T12:16:44.236">
  <compact-info movecount="8024461" movebytes="163375400" />
  <remembered-set-cleared processed="777104" cleared="777104" durationms="2.188" />
</gc-op>

The global cycle's increment ends. The end of the increment is recorded with <gc-end> and provides another snapshot of memory allocation on the heap, similar to <gc-start>.

<gc-end id="2020" type="global garbage collect" contextid="2003" durationms="1126.788" usertimems="7971.788" systemtimems="1.000" stalltimems="1016.256" timestamp="2021-03-05T12:16:44.237" activeThreads="8">
  <mem-info id="2021" free="1572864" total="838860800" percent="0">
    <mem type="eden" free="1572864" total="1572864" percent="100" />
    <pending-finalizers system="2" default="0" reference="0" classloader="0" />
    <remembered-set count="874496" freebytes="29884416" totalbytes="33382400" percent="89" regionsoverflowed="0" regionsstable="0" regionsrebuilding="0"/>
  </mem-info>
</gc-end>

Comparing the snapshot at the beginning and end of this STW global mark phase subincrement shows that memory was reclaimed and regions reassigned to create an empty eden space, equal to 1.5 MB (1,572,864 B). Because global cycles are triggered when memory conditions are tight, the global cycle is able to reclaim only a small amount of memory.

The cycle ends (<cycle-end>). The following <allocation-satisfied> element indicates that the allocation request that caused the allocation failure can now complete successfully.

<cycle-end id="2022" type="global garbage collect" contextid="2003" timestamp="2021-03-05T12:16:44.237" />
<allocation-satisfied id="2023" threadId="00000000008A9E00" bytesRequested="24" />
<af-end id="2024" timestamp="2021-03-05T12:16:44.237" threadId="00000000008AA780" success="true" />

The STW pause ends with the <exclusive-end> element.

<exclusive-end id="2025" timestamp="2021-03-05T12:16:44.237" durationms="1130.358" />

Summary

Analyzing the structure and elements of this example log output shows that this global cycle has the following characteristics: