aboutsummaryrefslogtreecommitdiff
path: root/spark-common/src/main/java/me/lucko/spark/monitor
diff options
context:
space:
mode:
authorLuck <git@lucko.me>2018-10-08 16:04:51 +0100
committerLuck <git@lucko.me>2018-10-08 16:04:51 +0100
commit648167064ad2064fc5ab77fb57b347253ac9d468 (patch)
tree5b44e40b144cdb013cc66e67a08b0208392fb6f5 /spark-common/src/main/java/me/lucko/spark/monitor
parenta342e45839970129ce5cdf1f5bad8da5c607106b (diff)
downloadspark-648167064ad2064fc5ab77fb57b347253ac9d468.tar.gz
spark-648167064ad2064fc5ab77fb57b347253ac9d468.tar.bz2
spark-648167064ad2064fc5ab77fb57b347253ac9d468.zip
reorganise some packages
Diffstat (limited to 'spark-common/src/main/java/me/lucko/spark/monitor')
-rw-r--r--spark-common/src/main/java/me/lucko/spark/monitor/GarbageCollectionMonitor.java77
-rw-r--r--spark-common/src/main/java/me/lucko/spark/monitor/TickMonitor.java139
2 files changed, 216 insertions, 0 deletions
diff --git a/spark-common/src/main/java/me/lucko/spark/monitor/GarbageCollectionMonitor.java b/spark-common/src/main/java/me/lucko/spark/monitor/GarbageCollectionMonitor.java
new file mode 100644
index 0000000..906ca07
--- /dev/null
+++ b/spark-common/src/main/java/me/lucko/spark/monitor/GarbageCollectionMonitor.java
@@ -0,0 +1,77 @@
+/*
+ * 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.monitor;
+
+import com.sun.management.GarbageCollectionNotificationInfo;
+
+import java.lang.management.GarbageCollectorMXBean;
+import java.lang.management.ManagementFactory;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.management.ListenerNotFoundException;
+import javax.management.Notification;
+import javax.management.NotificationEmitter;
+import javax.management.NotificationListener;
+import javax.management.openmbean.CompositeData;
+
+public class GarbageCollectionMonitor implements NotificationListener, AutoCloseable {
+
+ private final TickMonitor tickMonitor;
+ private final List<NotificationEmitter> emitters = new ArrayList<>();
+
+ public GarbageCollectionMonitor(TickMonitor tickMonitor) {
+ this.tickMonitor = tickMonitor;
+
+ List<GarbageCollectorMXBean> beans = ManagementFactory.getGarbageCollectorMXBeans();
+ for (GarbageCollectorMXBean bean : beans) {
+ if (!(bean instanceof NotificationEmitter)) {
+ continue;
+ }
+
+ NotificationEmitter notificationEmitter = (NotificationEmitter) bean;
+ notificationEmitter.addNotificationListener(this, null, null);
+ this.emitters.add(notificationEmitter);
+ }
+ }
+
+ @Override
+ public void handleNotification(Notification notification, Object handback) {
+ if (!notification.getType().equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION)) {
+ return;
+ }
+
+ GarbageCollectionNotificationInfo data = GarbageCollectionNotificationInfo.from((CompositeData) notification.getUserData());
+ this.tickMonitor.onGc(data);
+ }
+
+ @Override
+ public void close() {
+ for (NotificationEmitter e : this.emitters) {
+ try {
+ e.removeNotificationListener(this);
+ } catch (ListenerNotFoundException ex) {
+ ex.printStackTrace();
+ }
+ }
+ this.emitters.clear();
+ }
+}
diff --git a/spark-common/src/main/java/me/lucko/spark/monitor/TickMonitor.java b/spark-common/src/main/java/me/lucko/spark/monitor/TickMonitor.java
new file mode 100644
index 0000000..a7dd4a8
--- /dev/null
+++ b/spark-common/src/main/java/me/lucko/spark/monitor/TickMonitor.java
@@ -0,0 +1,139 @@
+/*
+ * 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.monitor;
+
+import com.sun.management.GarbageCollectionNotificationInfo;
+
+import me.lucko.spark.sampler.TickCounter;
+
+import java.text.DecimalFormat;
+import java.util.DoubleSummaryStatistics;
+
+public abstract class TickMonitor implements Runnable, AutoCloseable {
+ private static final DecimalFormat df = new DecimalFormat("#.##");
+
+ private final TickCounter tickCounter;
+ private final GarbageCollectionMonitor garbageCollectionMonitor;
+ private final int percentageChangeThreshold;
+
+ // data
+ private volatile double lastTickTime = 0;
+ private State state = null;
+ private DoubleSummaryStatistics averageTickTime = new DoubleSummaryStatistics();
+ private double avg;
+
+ public TickMonitor(TickCounter tickCounter, int percentageChangeThreshold) {
+ this.tickCounter = tickCounter;
+ this.percentageChangeThreshold = percentageChangeThreshold;
+
+ this.tickCounter.start();
+ this.tickCounter.addTickTask(this);
+
+ this.garbageCollectionMonitor = new GarbageCollectionMonitor(this);
+ }
+
+ protected abstract void sendMessage(String message);
+
+ @Override
+ public void close() {
+ this.tickCounter.close();
+ this.garbageCollectionMonitor.close();
+ }
+
+ @Override
+ public void run() {
+ double now = ((double) System.nanoTime()) / 1000000d;
+
+ // init
+ if (this.state == null) {
+ this.state = State.SETUP;
+ this.lastTickTime = now;
+ sendMessage("Tick monitor started. Before the monitor becomes fully active, the server's " +
+ "average tick rate will be calculated over a period of 120 ticks (approx 6 seconds).");
+ return;
+ }
+
+ // find the diff
+ double last = this.lastTickTime;
+ double diff = now - last;
+ boolean ignore = last == 0;
+ this.lastTickTime = now;
+
+ if (ignore) {
+ return;
+ }
+
+ // form averages
+ if (this.state == State.SETUP) {
+ this.averageTickTime.accept(diff);
+
+ // move onto the next state
+ if (this.averageTickTime.getCount() >= 120) {
+
+ sendMessage("&bAnalysis is now complete.");
+ sendMessage("&f> &7Max: " + df.format(this.averageTickTime.getMax()) + "ms");
+ sendMessage("&f> &7Min: " + df.format(this.averageTickTime.getMin()) + "ms");
+ sendMessage("&f> &7Avg: " + df.format(this.averageTickTime.getAverage()) + "ms");
+ sendMessage("Starting now, any ticks with >" + this.percentageChangeThreshold + "% increase in " +
+ "duration compared to the average will be reported.");
+
+ this.avg = this.averageTickTime.getAverage();
+ this.state = State.MONITORING;
+ }
+ }
+
+ if (this.state == State.MONITORING) {
+ double increase = diff - this.avg;
+ if (increase <= 0) {
+ return;
+ }
+
+ double percentageChange = (increase * 100d) / this.avg;
+ if (percentageChange > this.percentageChangeThreshold) {
+ sendMessage("&7Tick &8#" + this.tickCounter.getCurrentTick() + " &7lasted &b" + df.format(diff) +
+ "&7 ms. (&b" + df.format(percentageChange) + "% &7increase from average)");
+ }
+ }
+ }
+
+ void onGc(GarbageCollectionNotificationInfo data) {
+ if (this.state == State.SETUP) {
+ // set lastTickTime to zero so this tick won't be counted in the average
+ this.lastTickTime = 0;
+ return;
+ }
+
+ String gcType = data.getGcAction();
+ if (gcType.equals("end of minor GC")) {
+ gcType = "Young Gen GC";
+ } else if (gcType.equals("end of major GC")) {
+ gcType = "Old Gen GC";
+ }
+
+ sendMessage("&7Tick &8#" + this.tickCounter.getCurrentTick() + " &7included &4GC &7lasting &b" +
+ df.format(data.getGcInfo().getDuration()) + "&7 ms. (type = " + gcType + ")");
+ }
+
+ private enum State {
+ SETUP,
+ MONITORING
+ }
+}