aboutsummaryrefslogtreecommitdiff
path: root/spark-common
diff options
context:
space:
mode:
authorLuck <git@lucko.me>2020-04-11 16:09:11 +0100
committerLuck <git@lucko.me>2020-04-11 16:09:11 +0100
commitefa16aacebe904a5052f9946652e22bd453e39bb (patch)
tree9b57ae6fc33746433c3169923d915a2060129223 /spark-common
parenta235245e92cdeefecbd0bba33c68c0141415a359 (diff)
downloadspark-efa16aacebe904a5052f9946652e22bd453e39bb.tar.gz
spark-efa16aacebe904a5052f9946652e22bd453e39bb.tar.bz2
spark-efa16aacebe904a5052f9946652e22bd453e39bb.zip
Report average collection frequency in '/spark gc'
Diffstat (limited to 'spark-common')
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/SparkPlatform.java19
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/modules/GcMonitoringModule.java69
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/monitor/memory/GarbageCollectorStatistics.java81
3 files changed, 151 insertions, 18 deletions
diff --git a/spark-common/src/main/java/me/lucko/spark/common/SparkPlatform.java b/spark-common/src/main/java/me/lucko/spark/common/SparkPlatform.java
index 50ef7d8..4d6557b 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/SparkPlatform.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/SparkPlatform.java
@@ -21,6 +21,7 @@
package me.lucko.spark.common;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import me.lucko.spark.common.activitylog.ActivityLog;
import me.lucko.spark.common.command.Arguments;
import me.lucko.spark.common.command.Command;
@@ -36,6 +37,7 @@ import me.lucko.spark.common.command.sender.CommandSender;
import me.lucko.spark.common.command.tabcomplete.CompletionSupplier;
import me.lucko.spark.common.command.tabcomplete.TabCompleter;
import me.lucko.spark.common.monitor.cpu.CpuMonitor;
+import me.lucko.spark.common.monitor.memory.GarbageCollectorStatistics;
import me.lucko.spark.common.monitor.tick.TickStatistics;
import me.lucko.spark.common.sampler.tick.TickHook;
import me.lucko.spark.common.sampler.tick.TickReporter;
@@ -50,6 +52,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.stream.Collectors;
/**
@@ -71,6 +74,8 @@ public class SparkPlatform {
private final TickHook tickHook;
private final TickReporter tickReporter;
private final TickStatistics tickStatistics;
+ private Map<String, GarbageCollectorStatistics> startupGcStatistics = ImmutableMap.of();
+ private long serverNormalOperationStartTime;
public SparkPlatform(SparkPlugin plugin) {
this.plugin = plugin;
@@ -108,6 +113,12 @@ public class SparkPlatform {
this.tickReporter.start();
}
CpuMonitor.ensureMonitoring();
+
+ // poll startup GC statistics after plugins & the world have loaded
+ this.plugin.executeAsync(() -> {
+ this.startupGcStatistics = GarbageCollectorStatistics.pollStats();
+ this.serverNormalOperationStartTime = System.currentTimeMillis();
+ });
}
public void disable() {
@@ -143,6 +154,14 @@ public class SparkPlatform {
return this.tickStatistics;
}
+ public Map<String, GarbageCollectorStatistics> getStartupGcStatistics() {
+ return this.startupGcStatistics;
+ }
+
+ public long getServerNormalOperationStartTime() {
+ return this.serverNormalOperationStartTime;
+ }
+
public void executeCommand(CommandSender sender, String[] args) {
CommandResponseHandler resp = new CommandResponseHandler(this, sender);
diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/modules/GcMonitoringModule.java b/spark-common/src/main/java/me/lucko/spark/common/command/modules/GcMonitoringModule.java
index 2e67a99..6330797 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/command/modules/GcMonitoringModule.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/command/modules/GcMonitoringModule.java
@@ -26,14 +26,13 @@ import me.lucko.spark.common.command.Command;
import me.lucko.spark.common.command.CommandModule;
import me.lucko.spark.common.command.CommandResponseHandler;
import me.lucko.spark.common.monitor.memory.GarbageCollectionMonitor;
+import me.lucko.spark.common.monitor.memory.GarbageCollectorStatistics;
import me.lucko.spark.common.util.FormatUtil;
import net.kyori.text.Component;
import net.kyori.text.TextComponent;
import net.kyori.text.format.TextColor;
import net.kyori.text.format.TextDecoration;
-import java.lang.management.GarbageCollectorMXBean;
-import java.lang.management.ManagementFactory;
import java.lang.management.MemoryUsage;
import java.text.DecimalFormat;
import java.util.LinkedList;
@@ -60,26 +59,30 @@ public class GcMonitoringModule implements CommandModule {
consumer.accept(Command.builder()
.aliases("gc")
.executor((platform, sender, resp, arguments) -> {
- resp.replyPrefixed(TextComponent.of("Calculating GC collection averages..."));
+ resp.replyPrefixed(TextComponent.of("Calculating GC statistics..."));
List<Component> report = new LinkedList<>();
report.add(TextComponent.empty());
report.add(TextComponent.builder("")
.append(TextComponent.builder(">").color(TextColor.DARK_GRAY).decoration(TextDecoration.BOLD, true).build())
.append(TextComponent.space())
- .append(TextComponent.of("GC collection averages", TextColor.GOLD))
+ .append(TextComponent.of("Garbage Collector statistics", TextColor.GOLD))
.build()
);
- for (GarbageCollectorMXBean collector : ManagementFactory.getGarbageCollectorMXBeans()) {
- double collectionTime = collector.getCollectionTime();
- long collectionCount = collector.getCollectionCount();
+ long serverUptime = System.currentTimeMillis() - platform.getServerNormalOperationStartTime();
+ Map<String, GarbageCollectorStatistics> collectorStats = GarbageCollectorStatistics.pollStatsSubtractInitial(platform.getStartupGcStatistics());
+
+ for (Map.Entry<String, GarbageCollectorStatistics> collector : collectorStats.entrySet()) {
+ String collectorName = collector.getKey();
+ double collectionTime = collector.getValue().getCollectionTime();
+ long collectionCount = collector.getValue().getCollectionCount();
report.add(TextComponent.empty());
if (collectionCount == 0) {
report.add(TextComponent.builder(" ")
- .append(TextComponent.of(collector.getName() + " collector:", TextColor.GRAY))
+ .append(TextComponent.of(collectorName + " collector:", TextColor.GRAY))
.build()
);
report.add(TextComponent.builder(" ")
@@ -91,9 +94,10 @@ public class GcMonitoringModule implements CommandModule {
}
double averageCollectionTime = collectionTime / collectionCount;
+ double averageFrequency = (serverUptime - collectionTime) / collectionCount;
report.add(TextComponent.builder(" ")
- .append(TextComponent.of(collector.getName() + " collector:", TextColor.GRAY))
+ .append(TextComponent.of(collectorName + " collector:", TextColor.GRAY))
.build()
);
report.add(TextComponent.builder(" ")
@@ -104,6 +108,11 @@ public class GcMonitoringModule implements CommandModule {
.append(TextComponent.of(" total collections", TextColor.GRAY))
.build()
);
+ report.add(TextComponent.builder(" ")
+ .append(TextComponent.of(formatTime((long) averageFrequency), TextColor.WHITE))
+ .append(TextComponent.of(" avg frequency", TextColor.GRAY))
+ .build()
+ );
}
if (report.size() == 1) {
@@ -130,6 +139,30 @@ public class GcMonitoringModule implements CommandModule {
);
}
+ private static String formatTime(long millis) {
+ if (millis <= 0) {
+ return "0ms";
+ }
+
+ long second = millis / 1000;
+ millis = millis % 1000;
+ long minute = second / 60;
+ second = second % 60;
+
+ StringBuilder sb = new StringBuilder();
+ if (minute != 0) {
+ sb.append(minute).append("m ");
+ }
+ if (second != 0) {
+ sb.append(second).append("s ");
+ }
+ if (millis != 0) {
+ sb.append(millis).append("ms");
+ }
+
+ return sb.toString().trim();
+ }
+
private static class ReportingGcMonitor extends GarbageCollectionMonitor implements GarbageCollectionMonitor.Listener {
private final SparkPlatform platform;
private final CommandResponseHandler resp;
@@ -193,13 +226,13 @@ public class GcMonitoringModule implements CommandModule {
.build()
);
report.add(TextComponent.builder(" ")
- .append(TextComponent.of(FormatUtil.formatBytes(before.getUsed()), TextColor.WHITE))
- .append(TextComponent.of(" → ", TextColor.GRAY))
- .append(TextComponent.of(FormatUtil.formatBytes(after.getUsed()), TextColor.WHITE))
+ .append(TextComponent.of(FormatUtil.formatBytes(before.getUsed()), TextColor.GRAY))
+ .append(TextComponent.of(" → ", TextColor.DARK_GRAY))
+ .append(TextComponent.of(FormatUtil.formatBytes(after.getUsed()), TextColor.GRAY))
.append(TextComponent.space())
- .append(TextComponent.of("(", TextColor.GRAY))
- .append(TextComponent.of(FormatUtil.percent(diff, before.getUsed()), TextColor.GREEN))
- .append(TextComponent.of(")", TextColor.GRAY))
+ .append(TextComponent.of("(", TextColor.DARK_GRAY))
+ .append(TextComponent.of(FormatUtil.percent(diff, before.getUsed()), TextColor.WHITE))
+ .append(TextComponent.of(")", TextColor.DARK_GRAY))
.build()
);
} else {
@@ -210,9 +243,9 @@ public class GcMonitoringModule implements CommandModule {
.build()
);
report.add(TextComponent.builder(" ")
- .append(TextComponent.of(FormatUtil.formatBytes(before.getUsed()), TextColor.WHITE))
- .append(TextComponent.of(" → ", TextColor.GRAY))
- .append(TextComponent.of(FormatUtil.formatBytes(after.getUsed()), TextColor.WHITE))
+ .append(TextComponent.of(FormatUtil.formatBytes(before.getUsed()), TextColor.GRAY))
+ .append(TextComponent.of(" → ", TextColor.DARK_GRAY))
+ .append(TextComponent.of(FormatUtil.formatBytes(after.getUsed()), TextColor.GRAY))
.build()
);
}
diff --git a/spark-common/src/main/java/me/lucko/spark/common/monitor/memory/GarbageCollectorStatistics.java b/spark-common/src/main/java/me/lucko/spark/common/monitor/memory/GarbageCollectorStatistics.java
new file mode 100644
index 0000000..e875fd7
--- /dev/null
+++ b/spark-common/src/main/java/me/lucko/spark/common/monitor/memory/GarbageCollectorStatistics.java
@@ -0,0 +1,81 @@
+/*
+ * This file is part of spark.
+ *
+ * Copyright (c) lucko (Luck) <luck@lucko.me>
+ * Copyright (c) contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package me.lucko.spark.common.monitor.memory;
+
+import com.google.common.collect.ImmutableMap;
+
+import java.lang.management.GarbageCollectorMXBean;
+import java.lang.management.ManagementFactory;
+import java.util.Map;
+
+/**
+ * Holder for {@link GarbageCollectorMXBean} statistics.
+ */
+public class GarbageCollectorStatistics {
+ public static final GarbageCollectorStatistics ZERO = new GarbageCollectorStatistics(0, 0);
+
+ public static Map<String, GarbageCollectorStatistics> pollStats() {
+ ImmutableMap.Builder<String, GarbageCollectorStatistics> stats = ImmutableMap.builder();
+ for (GarbageCollectorMXBean bean : ManagementFactory.getGarbageCollectorMXBeans()) {
+ stats.put(bean.getName(), new GarbageCollectorStatistics(bean));
+ }
+ return stats.build();
+ }
+
+ public static Map<String, GarbageCollectorStatistics> pollStatsSubtractInitial(Map<String, GarbageCollectorStatistics> initial) {
+ ImmutableMap.Builder<String, GarbageCollectorStatistics> stats = ImmutableMap.builder();
+ for (GarbageCollectorMXBean bean : ManagementFactory.getGarbageCollectorMXBeans()) {
+ stats.put(bean.getName(), new GarbageCollectorStatistics(bean).subtract(initial.getOrDefault(bean.getName(), ZERO)));
+ }
+ return stats.build();
+ }
+
+ private final long collectionCount;
+ private final long collectionTime;
+
+ public GarbageCollectorStatistics(long collectionCount, long collectionTime) {
+ this.collectionCount = collectionCount;
+ this.collectionTime = collectionTime;
+ }
+
+ public GarbageCollectorStatistics(GarbageCollectorMXBean bean) {
+ this(bean.getCollectionCount(), bean.getCollectionTime());
+ }
+
+ public long getCollectionCount() {
+ return this.collectionCount;
+ }
+
+ public long getCollectionTime() {
+ return this.collectionTime;
+ }
+
+ public GarbageCollectorStatistics subtract(GarbageCollectorStatistics other) {
+ if (other == ZERO || (other.collectionCount == 0 && other.collectionTime == 0)) {
+ return this;
+ }
+
+ return new GarbageCollectorStatistics(
+ this.collectionCount - other.collectionCount,
+ this.collectionTime - other.collectionTime
+ );
+ }
+}