aboutsummaryrefslogtreecommitdiff
path: root/spark-common/src
diff options
context:
space:
mode:
Diffstat (limited to 'spark-common/src')
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/GarbageCollectionMonitor.java77
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/TickMonitor.java41
-rw-r--r--spark-common/src/main/java/me/lucko/spark/profiler/TickCounter.java3
3 files changed, 115 insertions, 6 deletions
diff --git a/spark-common/src/main/java/me/lucko/spark/common/GarbageCollectionMonitor.java b/spark-common/src/main/java/me/lucko/spark/common/GarbageCollectionMonitor.java
new file mode 100644
index 0000000..dd9931e
--- /dev/null
+++ b/spark-common/src/main/java/me/lucko/spark/common/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.common;
+
+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/common/TickMonitor.java b/spark-common/src/main/java/me/lucko/spark/common/TickMonitor.java
index 98f7a28..6dc84ee 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/TickMonitor.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/TickMonitor.java
@@ -20,19 +20,22 @@
package me.lucko.spark.common;
+import com.sun.management.GarbageCollectionNotificationInfo;
+
import me.lucko.spark.profiler.TickCounter;
import java.text.DecimalFormat;
import java.util.DoubleSummaryStatistics;
-public abstract class TickMonitor implements Runnable {
+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 double lastTickTime = 0;
+ private volatile double lastTickTime = 0;
private State state = null;
private DoubleSummaryStatistics averageTickTime = new DoubleSummaryStatistics();
private double avg;
@@ -43,12 +46,16 @@ public abstract class TickMonitor implements Runnable {
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
@@ -65,9 +72,15 @@ public abstract class TickMonitor implements Runnable {
}
// find the diff
- double diff = now - this.lastTickTime;
+ 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);
@@ -95,12 +108,30 @@ public abstract class TickMonitor implements Runnable {
double percentageChange = (increase * 100d) / this.avg;
if (percentageChange > this.percentageChangeThreshold) {
- sendMessage("&7Tick &8#" + this.tickCounter.getCurrentTick() + " &7lasted &b" + df.format(diff) + "&7 milliseconds. " +
- "&7(&b" + df.format(percentageChange) + "% &7increase from average)");
+ 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
diff --git a/spark-common/src/main/java/me/lucko/spark/profiler/TickCounter.java b/spark-common/src/main/java/me/lucko/spark/profiler/TickCounter.java
index ac804f8..31d7f9a 100644
--- a/spark-common/src/main/java/me/lucko/spark/profiler/TickCounter.java
+++ b/spark-common/src/main/java/me/lucko/spark/profiler/TickCounter.java
@@ -23,7 +23,7 @@ package me.lucko.spark.profiler;
/**
* A hook with the game's "tick loop".
*/
-public interface TickCounter {
+public interface TickCounter extends AutoCloseable {
/**
* Starts the counter
@@ -33,6 +33,7 @@ public interface TickCounter {
/**
* Stops the counter
*/
+ @Override
void close();
/**