diff options
author | Luck <git@lucko.me> | 2018-06-29 22:58:19 +0100 |
---|---|---|
committer | Luck <git@lucko.me> | 2018-06-29 22:58:19 +0100 |
commit | fa2a1e5661ef40323a36283fbf913b6a1258950b (patch) | |
tree | 17dbe91c52a9a8080036e1e2463490ea2e867adb /spark-common/src/main/java/me | |
parent | 4e150e2ff03181b32b6fcfdbd9c6253fc099fa58 (diff) | |
download | spark-fa2a1e5661ef40323a36283fbf913b6a1258950b.tar.gz spark-fa2a1e5661ef40323a36283fbf913b6a1258950b.tar.bz2 spark-fa2a1e5661ef40323a36283fbf913b6a1258950b.zip |
Implement GC notifications as part of the monitoring command
Diffstat (limited to 'spark-common/src/main/java/me')
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(); /** |