G1PeriodicGc.java
package io.github.baokhang83.mnemo.warden.agent.heap;
import com.sun.management.HotSpotDiagnosticMXBean;
import io.github.baokhang83.mnemo.warden.agent.attach.AttachedJvm;
import java.io.IOException;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.time.Duration;
import java.util.List;
/**
* Reads and writes G1's idle-triggered collection interval ({@code G1PeriodicGCInterval}) on an
* attached target.
*
* <p>G1 has no runtime soft max ({@link SoftMax} already rejects it) and {@link DeepGc} already
* covers on-demand uncommit — this is G1's one collector-specific lever: verified against a
* real target that the flag is {@code {manageable}} (settable at runtime over JMX, same mechanism
* as {@code SoftMaxHeapSize}) but defaults to {@code 0} (disabled), meaning G1 never proactively
* collects on idle unless told to.
*
* <p>Unlike {@link SoftMax}/{@link DeepGc}, this flag doesn't exist on ZGC or Shenandoah —
* ZGC's concurrent cycle runs continuously, with no "idle" concept to trigger on — so {@link
* #forTarget(AttachedJvm)} checks the collector directly rather than adding a single-caller field
* to {@link GcCapabilities}.
*/
public final class G1PeriodicGc {
private static final String OPTION = "G1PeriodicGCInterval";
private final HotSpotDiagnosticMXBean diagnostics;
private G1PeriodicGc(HotSpotDiagnosticMXBean diagnostics) {
this.diagnostics = diagnostics;
}
/**
* @throws UnsupportedCollectorException if the target isn't running G1
* @throws IOException if the JMX connection to the target fails
*/
public static G1PeriodicGc forTarget(AttachedJvm target) throws IOException {
List<GarbageCollectorMXBean> gcBeans =
ManagementFactory.getPlatformMXBeans(target.mbeanConnection(), GarbageCollectorMXBean.class);
Collector collector = GcDetector.detect(gcBeans).collector();
if (collector != Collector.G1) {
throw new UnsupportedCollectorException(OPTION, collector);
}
HotSpotDiagnosticMXBean diagnostics =
ManagementFactory.getPlatformMXBean(target.mbeanConnection(), HotSpotDiagnosticMXBean.class);
return new G1PeriodicGc(diagnostics);
}
/** The current idle-triggered collection interval. {@link Duration#ZERO} means disabled. */
public Duration periodicGcInterval() {
return Duration.ofMillis(Long.parseLong(diagnostics.getVMOption(OPTION).getValue()));
}
/** Sets the idle-triggered collection interval. Takes effect immediately; no restart needed. */
public void setPeriodicGcInterval(Duration interval) {
diagnostics.setVMOption(OPTION, Long.toString(interval.toMillis()));
}
}