PerformanceAnalysisManager moved to Pandalib
This commit is contained in:
parent
35260ff54c
commit
5b20cb4372
@ -0,0 +1,475 @@
|
|||||||
|
package fr.pandacube.lib.paper.modules;
|
||||||
|
|
||||||
|
import com.destroystokyo.paper.event.server.ServerTickEndEvent;
|
||||||
|
import com.destroystokyo.paper.event.server.ServerTickStartEvent;
|
||||||
|
import fr.pandacube.lib.chat.Chat;
|
||||||
|
import fr.pandacube.lib.chat.ChatColorGradient;
|
||||||
|
import fr.pandacube.lib.chat.ChatColorUtil;
|
||||||
|
import fr.pandacube.lib.chat.ChatConfig.PandaTheme;
|
||||||
|
import fr.pandacube.lib.paper.PandaLibPaper;
|
||||||
|
import fr.pandacube.lib.paper.players.PaperOffPlayer;
|
||||||
|
import fr.pandacube.lib.paper.players.PaperOnlinePlayer;
|
||||||
|
import fr.pandacube.lib.paper.scheduler.SchedulerUtil;
|
||||||
|
import fr.pandacube.lib.paper.util.AutoUpdatedBossBar;
|
||||||
|
import fr.pandacube.lib.paper.util.AutoUpdatedBossBar.BarUpdater;
|
||||||
|
import fr.pandacube.lib.players.standalone.AbstractPlayerManager;
|
||||||
|
import fr.pandacube.lib.util.Log;
|
||||||
|
import fr.pandacube.lib.util.MemoryUtil;
|
||||||
|
import fr.pandacube.lib.util.MemoryUtil.MemoryUnit;
|
||||||
|
import fr.pandacube.lib.util.TimeUtil;
|
||||||
|
import net.kyori.adventure.bossbar.BossBar;
|
||||||
|
import net.kyori.adventure.bossbar.BossBar.Color;
|
||||||
|
import net.kyori.adventure.bossbar.BossBar.Overlay;
|
||||||
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
|
import net.kyori.adventure.text.format.TextColor;
|
||||||
|
import net.md_5.bungee.api.ChatColor;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.command.ConsoleCommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.EventPriority;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.player.PlayerJoinEvent;
|
||||||
|
import org.bukkit.event.player.PlayerQuitEvent;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
|
||||||
|
import java.lang.management.ManagementFactory;
|
||||||
|
import java.lang.management.ThreadMXBean;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static fr.pandacube.lib.chat.ChatStatic.chat;
|
||||||
|
import static fr.pandacube.lib.chat.ChatStatic.failureText;
|
||||||
|
import static fr.pandacube.lib.chat.ChatStatic.infoText;
|
||||||
|
import static fr.pandacube.lib.chat.ChatStatic.successText;
|
||||||
|
import static fr.pandacube.lib.chat.ChatStatic.text;
|
||||||
|
|
||||||
|
public class PerformanceAnalysisManager implements Listener {
|
||||||
|
|
||||||
|
private static PerformanceAnalysisManager instance;
|
||||||
|
|
||||||
|
public static synchronized PerformanceAnalysisManager getInstance() {
|
||||||
|
if (instance == null)
|
||||||
|
instance = new PerformanceAnalysisManager();
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static final int NB_TICK_HISTORY = 20 * 60 * 60; // 60 secondes;
|
||||||
|
|
||||||
|
private final Plugin plugin = PandaLibPaper.getPlugin();
|
||||||
|
private long firstRecord = 0;
|
||||||
|
|
||||||
|
private final ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
|
||||||
|
|
||||||
|
private long tickStartNanoTime = System.nanoTime();
|
||||||
|
private long tickStartCPUTime = 0;
|
||||||
|
private long tickEndNanoTime = System.nanoTime();
|
||||||
|
private long lastInterTPSDuration = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private final LinkedList<Long> tpsTimes = new LinkedList<>();
|
||||||
|
private final LinkedList<Long> tpsDurations = new LinkedList<>();
|
||||||
|
private final LinkedList<Long> tpsCPUTimes = new LinkedList<>();
|
||||||
|
private final LinkedList<Long> interTPSDurations = new LinkedList<>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private final AutoUpdatedBossBar tpsBar;
|
||||||
|
private final AutoUpdatedBossBar memoryBar;
|
||||||
|
private final List<Player> barPlayers = new ArrayList<>();
|
||||||
|
private final List<BossBar> relatedBossBars = new ArrayList<>();
|
||||||
|
|
||||||
|
|
||||||
|
public final ChatColorGradient tps1sGradient = new ChatColorGradient()
|
||||||
|
.add(0, NamedTextColor.BLACK)
|
||||||
|
.add(1, NamedTextColor.DARK_RED)
|
||||||
|
.add(5, NamedTextColor.RED)
|
||||||
|
.add(10, NamedTextColor.GOLD)
|
||||||
|
.add(14, NamedTextColor.YELLOW)
|
||||||
|
.add(20, PandaTheme.CHAT_DECORATION_COLOR)
|
||||||
|
.add(26, NamedTextColor.BLUE);
|
||||||
|
|
||||||
|
|
||||||
|
public final ChatColorGradient tps10sGradient = new ChatColorGradient()
|
||||||
|
.add(0, NamedTextColor.DARK_RED)
|
||||||
|
.add(5, NamedTextColor.RED)
|
||||||
|
.add(10, NamedTextColor.GOLD)
|
||||||
|
.add(14, NamedTextColor.YELLOW)
|
||||||
|
.add(18, PandaTheme.CHAT_DECORATION_COLOR);
|
||||||
|
|
||||||
|
|
||||||
|
public final ChatColorGradient tps1mGradient = new ChatColorGradient()
|
||||||
|
.add(0, NamedTextColor.DARK_RED)
|
||||||
|
.add(8, NamedTextColor.RED)
|
||||||
|
.add(12, NamedTextColor.GOLD)
|
||||||
|
.add(16, NamedTextColor.YELLOW)
|
||||||
|
.add(20, PandaTheme.CHAT_DECORATION_COLOR);
|
||||||
|
|
||||||
|
public final ChatColorGradient memoryUsageGradient = new ChatColorGradient()
|
||||||
|
.add(.60f, PandaTheme.CHAT_DECORATION_COLOR)
|
||||||
|
.add(.70f, NamedTextColor.YELLOW)
|
||||||
|
.add(.80f, NamedTextColor.GOLD)
|
||||||
|
.add(.90f, NamedTextColor.RED)
|
||||||
|
.add(.95f , NamedTextColor.DARK_RED);
|
||||||
|
|
||||||
|
|
||||||
|
private PerformanceAnalysisManager() {
|
||||||
|
|
||||||
|
Bukkit.getPluginManager().registerEvents(this, plugin);
|
||||||
|
|
||||||
|
BossBar bossBar = BossBar.bossBar(text("TPS Serveur"), 0, Color.GREEN, Overlay.NOTCHED_20);
|
||||||
|
tpsBar = new AutoUpdatedBossBar(bossBar, new TPSBossBarUpdater());
|
||||||
|
tpsBar.scheduleUpdateTimeSyncThreadAsync(1000, 100);
|
||||||
|
|
||||||
|
BossBar bossMemBar = BossBar.bossBar(text("Mémoire Serveur"), 0, Color.GREEN, Overlay.NOTCHED_10);
|
||||||
|
memoryBar = new AutoUpdatedBossBar(bossMemBar, new MemoryBossBarUpdater());
|
||||||
|
memoryBar.scheduleUpdateTimeSyncThreadAsync(1000, 100);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean barsContainsPlayer(Player p) {
|
||||||
|
return barPlayers.contains(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void addPlayerToBars(Player p) {
|
||||||
|
barPlayers.add(p);
|
||||||
|
p.showBossBar(tpsBar.bar);
|
||||||
|
p.showBossBar(memoryBar.bar);
|
||||||
|
for (BossBar bar : relatedBossBars)
|
||||||
|
p.showBossBar(bar);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void removePlayerToBars(Player p) {
|
||||||
|
p.hideBossBar(tpsBar.bar);
|
||||||
|
p.hideBossBar(memoryBar.bar);
|
||||||
|
for (BossBar bar : relatedBossBars)
|
||||||
|
p.hideBossBar(bar);
|
||||||
|
barPlayers.remove(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void addBossBar(BossBar bar) {
|
||||||
|
if (relatedBossBars.contains(bar))
|
||||||
|
return;
|
||||||
|
relatedBossBars.add(bar);
|
||||||
|
for (Player p : barPlayers)
|
||||||
|
p.showBossBar(bar);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void removeBossBar(BossBar bar) {
|
||||||
|
if (!relatedBossBars.contains(bar))
|
||||||
|
return;
|
||||||
|
relatedBossBars.remove(bar);
|
||||||
|
for (Player p : barPlayers)
|
||||||
|
p.hideBossBar(bar);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void cancelInternalBossBar() {
|
||||||
|
tpsBar.cancel();
|
||||||
|
memoryBar.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public synchronized void onTickStart(ServerTickStartEvent event) {
|
||||||
|
tickStartNanoTime = System.nanoTime();
|
||||||
|
tickStartCPUTime = threadMXBean.isThreadCpuTimeSupported() ? threadMXBean.getCurrentThreadCpuTime() : 0;
|
||||||
|
|
||||||
|
lastInterTPSDuration = firstRecord == 0 ? 0 : (tickStartNanoTime - tickEndNanoTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public synchronized void onTickEnd(ServerTickEndEvent event) {
|
||||||
|
tickEndNanoTime = System.nanoTime();
|
||||||
|
long tickEndCPUTime = threadMXBean.isThreadCpuTimeSupported() ? threadMXBean.getCurrentThreadCpuTime() : 0;
|
||||||
|
|
||||||
|
if (firstRecord == 0) firstRecord = System.currentTimeMillis();
|
||||||
|
|
||||||
|
tpsTimes.add(System.currentTimeMillis());
|
||||||
|
tpsDurations.add(tickEndNanoTime - tickStartNanoTime);
|
||||||
|
tpsCPUTimes.add(tickEndCPUTime - tickStartCPUTime);
|
||||||
|
interTPSDurations.add(lastInterTPSDuration);
|
||||||
|
|
||||||
|
while (tpsTimes.size() > NB_TICK_HISTORY + 1)
|
||||||
|
tpsTimes.poll();
|
||||||
|
while (tpsDurations.size() > NB_TICK_HISTORY + 1)
|
||||||
|
tpsDurations.poll();
|
||||||
|
while (tpsCPUTimes.size() > NB_TICK_HISTORY + 1)
|
||||||
|
tpsCPUTimes.poll();
|
||||||
|
while (interTPSDurations.size() > NB_TICK_HISTORY + 1)
|
||||||
|
interTPSDurations.poll();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.MONITOR)
|
||||||
|
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||||
|
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
AbstractPlayerManager<PaperOnlinePlayer, PaperOffPlayer> playerManager = (AbstractPlayerManager<PaperOnlinePlayer, PaperOffPlayer>) AbstractPlayerManager.getInstance();
|
||||||
|
PaperOffPlayer offP = playerManager.getOffline(event.getPlayer().getUniqueId());
|
||||||
|
try {
|
||||||
|
if ("true".equals(offP.getConfig("system.bar", "false"))) {
|
||||||
|
SchedulerUtil.runOnServerThread(() -> addPlayerToBars(event.getPlayer()));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.severe("Cannot get player config", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.LOWEST)
|
||||||
|
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||||
|
removePlayerToBars(event.getPlayer());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private final long maxMem = Runtime.getRuntime().maxMemory();
|
||||||
|
|
||||||
|
|
||||||
|
private class MemoryBossBarUpdater implements BarUpdater {
|
||||||
|
@Override
|
||||||
|
public void update(AutoUpdatedBossBar bar) {
|
||||||
|
long allocMem = Runtime.getRuntime().totalMemory();
|
||||||
|
long freeMem = Runtime.getRuntime().freeMemory();
|
||||||
|
long usedMem = allocMem - freeMem;
|
||||||
|
|
||||||
|
double progress = usedMem / (double)maxMem;
|
||||||
|
progress = (progress < 0) ? 0 : (progress > 1) ? 1 : progress;
|
||||||
|
|
||||||
|
Color barColor = (progress >= 0.85) ? Color.RED
|
||||||
|
: (progress >= 0.65) ? Color.YELLOW
|
||||||
|
: Color.GREEN;
|
||||||
|
|
||||||
|
TextColor usedColor = memoryUsageGradient.pickColorAt((float)progress);
|
||||||
|
|
||||||
|
Chat display = infoText("Mémoire : ")
|
||||||
|
.then(text("Util:" + MemoryUtil.humanReadableSize(usedMem, MemoryUnit.MB, false)
|
||||||
|
+ "/" + MemoryUtil.humanReadableSize(maxMem, MemoryUnit.MB, false)
|
||||||
|
)
|
||||||
|
.color(usedColor)
|
||||||
|
)
|
||||||
|
.thenText(" Allouée:" + MemoryUtil.humanReadableSize(allocMem, MemoryUnit.MB, false));
|
||||||
|
|
||||||
|
bar.setColor(barColor);
|
||||||
|
bar.setProgress(progress);
|
||||||
|
bar.setTitle(display);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private class TPSBossBarUpdater implements BarUpdater {
|
||||||
|
@Override
|
||||||
|
public void update(AutoUpdatedBossBar bar) {
|
||||||
|
synchronized (PerformanceAnalysisManager.this) {
|
||||||
|
float tps1s = getTPS(1000);
|
||||||
|
|
||||||
|
Color barColor = (tps1s >= 25) ? Color.WHITE
|
||||||
|
: (tps1s >= 12) ? Color.GREEN
|
||||||
|
: (tps1s >= 6) ? Color.YELLOW
|
||||||
|
: Color.RED;
|
||||||
|
double barProgress = Double.isNaN(tps1s) ? 0 : tps1s/20d;
|
||||||
|
|
||||||
|
Chat title;
|
||||||
|
if (alteredTPSTitle != null) {
|
||||||
|
title = infoText("TPS : ").then(alteredTPSTitle);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
String tps1sDisp = Double.isNaN(tps1s) ? "N/A" : (Math.round(tps1s)) + "";
|
||||||
|
|
||||||
|
|
||||||
|
int[] tpsHistory = getTPSHistory();
|
||||||
|
|
||||||
|
// keep the legacy text when generating the bar to save space when converting to component
|
||||||
|
StringBuilder s = new StringBuilder();
|
||||||
|
ChatColor prevC = ChatColor.RESET;
|
||||||
|
for (int i = 58; i >= 0; i--) {
|
||||||
|
int t = tpsHistory[i];
|
||||||
|
ChatColor newC = ChatColorUtil.toBungee(tps1sGradient.pickColorAt(t));
|
||||||
|
if (newC != prevC) {
|
||||||
|
s.append(newC);
|
||||||
|
prevC = newC;
|
||||||
|
}
|
||||||
|
s.append("|");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// tick time measurement
|
||||||
|
Chat timings;
|
||||||
|
int nbTick1s = getTPS1s();
|
||||||
|
if (nbTick1s == 0) {
|
||||||
|
// we have a lag spike, so we need to display the time since lagging
|
||||||
|
long lagDurationSec = System.nanoTime() - tickEndNanoTime;
|
||||||
|
timings = text("(")
|
||||||
|
.thenFailure("lag:" + dispRound10(lagDurationSec / (double) 1_000_000_000) + "s")
|
||||||
|
.thenText(")");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
float avgTickDuration1s = getAvgNano(tpsDurations, nbTick1s)/1_000_000;
|
||||||
|
|
||||||
|
float avgTickCPUTime1s = getAvgNano(tpsCPUTimes, nbTick1s)/1_000_000;
|
||||||
|
TextColor avgTickCPUTime1sColor = (avgTickDuration1s < 46 || avgTickCPUTime1s < 20) ? PandaTheme.CHAT_DECORATION_COLOR
|
||||||
|
: (avgTickCPUTime1s < 30) ? NamedTextColor.YELLOW
|
||||||
|
: (avgTickCPUTime1s < 40) ? NamedTextColor.GOLD
|
||||||
|
: (avgTickCPUTime1s < 50) ? NamedTextColor.RED
|
||||||
|
: NamedTextColor.DARK_RED;
|
||||||
|
|
||||||
|
float avgTickWaitingTime1s = avgTickDuration1s - avgTickCPUTime1s;
|
||||||
|
TextColor avgTickWaitingTime1sColor = (avgTickDuration1s < 46 || avgTickWaitingTime1s < 20) ? PandaTheme.CHAT_DECORATION_COLOR
|
||||||
|
: (avgTickWaitingTime1s < 30) ? NamedTextColor.YELLOW
|
||||||
|
: (avgTickWaitingTime1s < 40) ? NamedTextColor.GOLD
|
||||||
|
: (avgTickWaitingTime1s < 50) ? NamedTextColor.RED
|
||||||
|
: NamedTextColor.DARK_RED;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
float avgInterTickDuration1s = getAvgNano(interTPSDurations, nbTick1s)/1_000_000;
|
||||||
|
TextColor avgInterTickDuration1sColor = (avgInterTickDuration1s > 10) ? PandaTheme.CHAT_DECORATION_COLOR
|
||||||
|
: (avgInterTickDuration1s > 4) ? NamedTextColor.YELLOW
|
||||||
|
: (avgTickDuration1s < 46) ? NamedTextColor.GOLD
|
||||||
|
: NamedTextColor.RED;
|
||||||
|
|
||||||
|
timings = text("(Tr:")
|
||||||
|
.then(text(Math.round(avgTickCPUTime1s) + "ms").color(avgTickCPUTime1sColor))
|
||||||
|
.thenText(" Tw:")
|
||||||
|
.then(text(Math.round(avgTickWaitingTime1s) + "ms").color(avgTickWaitingTime1sColor))
|
||||||
|
.thenText(" S:")
|
||||||
|
.then(text(Math.round(avgInterTickDuration1s) + "ms").color(avgInterTickDuration1sColor))
|
||||||
|
.thenText(")");
|
||||||
|
}
|
||||||
|
|
||||||
|
title = infoText("TPS [")
|
||||||
|
.thenLegacyText(s.toString())
|
||||||
|
.thenText("] ")
|
||||||
|
.then(text(tps1sDisp+"/20 ").color(tps1sGradient.pickColorAt(tps1s)))
|
||||||
|
.then(timings);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bar.setTitle(title);
|
||||||
|
bar.setColor(barColor);
|
||||||
|
bar.setProgress(Math.max(0, Math.min(1, barProgress)));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Chat alteredTPSTitle = null;
|
||||||
|
|
||||||
|
public synchronized void setAlteredTPSTitle(Chat title) {
|
||||||
|
alteredTPSTitle = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// special case where the getTPS method always returns a whole number when retrieving the TPS for 1 sec
|
||||||
|
public int getTPS1s() {
|
||||||
|
return (int) getTPS(1_000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param nbTicks number of ticks when the avg value is computed from history
|
||||||
|
* @return the avg number of TPS in the interval
|
||||||
|
*/
|
||||||
|
public synchronized float getAvgNano(List<Long> data, int nbTicks) {
|
||||||
|
if (data.size() <= 0) return 0;
|
||||||
|
|
||||||
|
if (nbTicks > data.size()) nbTicks = data.size();
|
||||||
|
|
||||||
|
long sum = 0;
|
||||||
|
for (int i = data.size() - nbTicks; i < data.size(); i++)
|
||||||
|
sum += data.get(i);
|
||||||
|
|
||||||
|
return sum / (float) nbTicks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param nbMillis number of milliseconds when the avg TPS is computed from history
|
||||||
|
* @return the avg number of TPS in the interval
|
||||||
|
*/
|
||||||
|
public synchronized float getTPS(long nbMillis) {
|
||||||
|
if (tpsTimes.size() == 0) return 0;
|
||||||
|
|
||||||
|
long currentMillis = System.currentTimeMillis();
|
||||||
|
|
||||||
|
if (currentMillis - nbMillis < firstRecord) nbMillis = currentMillis - firstRecord;
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
for (Long v : tpsTimes) {
|
||||||
|
if (v > currentMillis - nbMillis) count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count * (1000 / (float) nbMillis);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public synchronized int[] getTPSHistory() {
|
||||||
|
int[] history = new int[60];
|
||||||
|
|
||||||
|
long currentSec = System.currentTimeMillis() / 1000;
|
||||||
|
|
||||||
|
for (Long v : tpsTimes) {
|
||||||
|
int sec = (int) (currentSec - v/1000) - 1;
|
||||||
|
if (sec < 0 || sec >= 60)
|
||||||
|
continue;
|
||||||
|
history[sec]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return history;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static void gc(CommandSender sender) {
|
||||||
|
long t1 = System.currentTimeMillis();
|
||||||
|
long alloc1 = Runtime.getRuntime().totalMemory();
|
||||||
|
System.gc();
|
||||||
|
long t2 = System.currentTimeMillis();
|
||||||
|
long alloc2 = Runtime.getRuntime().totalMemory();
|
||||||
|
long released = alloc1 - alloc2;
|
||||||
|
Chat releasedMemoryMessage = released > 0
|
||||||
|
? successText(MemoryUtil.humanReadableSize(released) + " of memory released for the OS.")
|
||||||
|
: released < 0
|
||||||
|
? failureText(MemoryUtil.humanReadableSize(-released) + " of memory taken from the OS.")
|
||||||
|
: chat();
|
||||||
|
|
||||||
|
Chat finalMessage = successText("GC completed in " + TimeUtil.durationToString(t2 - t1, true) + ". ")
|
||||||
|
.then(releasedMemoryMessage);
|
||||||
|
if (sender != null)
|
||||||
|
sender.sendMessage(finalMessage);
|
||||||
|
if (!(sender instanceof ConsoleCommandSender))
|
||||||
|
Log.info(finalMessage.getLegacyText());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String dispRound10(double val) {
|
||||||
|
long v = (long) Math.ceil(val * 10);
|
||||||
|
return "" + (v / 10f);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user