aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/profiler/MemoryProfiler.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/profiler/MemoryProfiler.java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/profiler/MemoryProfiler.java100
1 files changed, 96 insertions, 4 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/profiler/MemoryProfiler.java b/src/main/java/com/google/devtools/build/lib/profiler/MemoryProfiler.java
index 0be0b1c9c6..59e995a3e0 100644
--- a/src/main/java/com/google/devtools/build/lib/profiler/MemoryProfiler.java
+++ b/src/main/java/com/google/devtools/build/lib/profiler/MemoryProfiler.java
@@ -14,10 +14,18 @@
package com.google.devtools.build.lib.profiler;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Splitter;
+import com.google.devtools.common.options.OptionsParsingException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.management.ManagementFactory;
+import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
+import java.time.Duration;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import javax.annotation.Nullable;
/**
* Blaze memory profiler.
@@ -46,10 +54,18 @@ public final class MemoryProfiler {
private PrintStream memoryProfile;
private ProfilePhase currentPhase;
+ private long heapUsedMemoryAtFinish;
+ @Nullable private MemoryProfileStableHeapParameters memoryProfileStableHeapParameters;
+
+ public synchronized void setStableMemoryParameters(
+ MemoryProfileStableHeapParameters memoryProfileStableHeapParameters) {
+ this.memoryProfileStableHeapParameters = memoryProfileStableHeapParameters;
+ }
public synchronized void start(OutputStream out) {
this.memoryProfile = (out == null) ? null : new PrintStream(out);
this.currentPhase = ProfilePhase.INIT;
+ heapUsedMemoryAtFinish = 0;
}
public synchronized void stop() {
@@ -57,19 +73,28 @@ public final class MemoryProfiler {
memoryProfile.close();
memoryProfile = null;
}
+ heapUsedMemoryAtFinish = 0;
}
- public synchronized void markPhase(ProfilePhase nextPhase) {
+ public synchronized long getHeapUsedMemoryAtFinish() {
+ return heapUsedMemoryAtFinish;
+ }
+
+ public synchronized void markPhase(ProfilePhase nextPhase) throws InterruptedException {
if (memoryProfile != null) {
+ MemoryMXBean bean = ManagementFactory.getMemoryMXBean();
+ prepareBean(nextPhase, bean, (duration) -> Thread.sleep(duration.toMillis()));
String name = currentPhase.description;
- ManagementFactory.getMemoryMXBean().gc();
- MemoryUsage memoryUsage = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
+ MemoryUsage memoryUsage = bean.getHeapMemoryUsage();
memoryProfile.println(name + ":heap:init:" + memoryUsage.getInit());
memoryProfile.println(name + ":heap:used:" + memoryUsage.getUsed());
memoryProfile.println(name + ":heap:commited:" + memoryUsage.getCommitted());
memoryProfile.println(name + ":heap:max:" + memoryUsage.getMax());
+ if (nextPhase == ProfilePhase.FINISH) {
+ heapUsedMemoryAtFinish = memoryUsage.getUsed();
+ }
- memoryUsage = ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage();
+ memoryUsage = bean.getNonHeapMemoryUsage();
memoryProfile.println(name + ":non-heap:init:" + memoryUsage.getInit());
memoryProfile.println(name + ":non-heap:used:" + memoryUsage.getUsed());
memoryProfile.println(name + ":non-heap:commited:" + memoryUsage.getCommitted());
@@ -77,4 +102,71 @@ public final class MemoryProfiler {
currentPhase = nextPhase;
}
}
+
+ @VisibleForTesting
+ synchronized void prepareBean(ProfilePhase nextPhase, MemoryMXBean bean, Sleeper sleeper)
+ throws InterruptedException {
+ bean.gc();
+ if (nextPhase == ProfilePhase.FINISH && memoryProfileStableHeapParameters != null) {
+ for (int i = 1; i < memoryProfileStableHeapParameters.numTimesToDoGc; i++) {
+ sleeper.sleep(memoryProfileStableHeapParameters.timeToSleepBetweenGcs);
+ bean.gc();
+ }
+ }
+ }
+
+ /**
+ * Parameters that control how {@code MemoryProfiler} tries to get a stable heap at the end of the
+ * build.
+ */
+ public static class MemoryProfileStableHeapParameters {
+ private final int numTimesToDoGc;
+ private final Duration timeToSleepBetweenGcs;
+
+ private MemoryProfileStableHeapParameters(int numTimesToDoGc, Duration timeToSleepBetweenGcs) {
+ this.numTimesToDoGc = numTimesToDoGc;
+ this.timeToSleepBetweenGcs = timeToSleepBetweenGcs;
+ }
+
+ /** Converter for {@code MemoryProfileStableHeapParameters} option. */
+ public static class Converter
+ implements com.google.devtools.common.options.Converter<MemoryProfileStableHeapParameters> {
+ private static final Splitter SPLITTER = Splitter.on(',');
+
+ @Override
+ public MemoryProfileStableHeapParameters convert(String input)
+ throws OptionsParsingException {
+ Iterator<String> values = SPLITTER.split(input).iterator();
+ try {
+ int numTimesToDoGc = Integer.parseInt(values.next());
+ int numSecondsToSleepBetweenGcs = Integer.parseInt(values.next());
+ if (values.hasNext()) {
+ throw new OptionsParsingException("Expected exactly 2 comma-separated integer values");
+ }
+ if (numTimesToDoGc <= 0) {
+ throw new OptionsParsingException("Number of times to GC must be positive");
+ }
+ if (numSecondsToSleepBetweenGcs < 0) {
+ throw new OptionsParsingException(
+ "Number of seconds to sleep between GC's must be positive");
+ }
+ return new MemoryProfileStableHeapParameters(
+ numTimesToDoGc, Duration.ofSeconds(numSecondsToSleepBetweenGcs));
+ } catch (NumberFormatException | NoSuchElementException nfe) {
+ throw new OptionsParsingException(
+ "Expected exactly 2 comma-separated integer values", nfe);
+ }
+ }
+
+ @Override
+ public String getTypeDescription() {
+ return "two integers, separated by a comma";
+ }
+ }
+ }
+
+ @VisibleForTesting
+ interface Sleeper {
+ void sleep(Duration duration) throws InterruptedException;
+ }
}