@@ -0,0 +1,2189 @@
+package com.github.alex1304.ultimategdbot.core;%0A%0Aimport java.lang.management.ManagementFactory;%0Aimport java.time.Duration;%0Aimport java.util.Optional;%0A%0Aimport javax.management.NotificationEmitter;%0Aimport javax.management.NotificationListener;%0Aimport javax.management.openmbean.CompositeData;%0A%0Aimport com.sun.management.GarbageCollectionNotificationInfo;%0A%0Aimport reactor.core.publisher.Flux;%0Aimport reactor.core.publisher.FluxSink;%0Aimport reactor.core.publisher.Mono;%0Aimport reactor.core.publisher.ReplayProcessor;%0A%0Aclass MemoryStats %7B%0A%09private static final ReplayProcessor%3CMemoryStats%3E STATS = ReplayProcessor.cacheLastOrDefault(new MemoryStats());%0A%09private static final FluxSink%3CMemoryStats%3E STATS_SINK = STATS.sink(FluxSink.OverflowStrategy.LATEST);%0A%09%0A%09private final long timestamp;%0A%09final long totalMemory;%0A%09final long usedMemory;%0A%09final long maxMemory;%0A%09%0A%09private MemoryStats(long timestamp) %7B%0A%09%09var total = Runtime.getRuntime().totalMemory();%0A%09%09var free = Runtime.getRuntime().freeMemory();%0A%09%09var max = Runtime.getRuntime().maxMemory();%0A%09%09this.timestamp = timestamp;%0A%09%09this.totalMemory = total;%0A%09%09this.usedMemory = total - free;%0A%09%09this.maxMemory = max;%0A%09%7D%0A%09%0A%09private MemoryStats() %7B%0A%09%09this.timestamp = -1;%0A%09%09this.totalMemory = Runtime.getRuntime().totalMemory();%0A%09%09this.usedMemory = 0;%0A%09%09this.maxMemory = Runtime.getRuntime().maxMemory();%0A%09%7D%0A%0A%09Optional%3CDuration%3E elapsedSinceLastGC() %7B%0A%09%09return Optional.of(timestamp)%0A%09%09%09%09.filter(t -%3E t %3E 0)%0A%09%09%09%09.map(t -%3E Duration.ofMillis(ManagementFactory.getRuntimeMXBean().getUptime() - t));%0A%09%7D%0A%09%0A%09static Mono%3CMemoryStats%3E getStats() %7B%0A%09%09return STATS.next();%0A%09%7D%0A%09%0A%09static void start() %7B%0A%09%09Flux.%3CMemoryStats%3Ecreate(sink -%3E %7B%0A%09%09%09NotificationListener gcListener = (notif, handback) -%3E %7B%0A%09%09%09%09if (notif.getType().equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION)) %7B%0A%09%09%09%09%09var gcInfo = GarbageCollectionNotificationInfo.from((CompositeData) notif.getUserData()).getGcInfo();%0A%09%09%09%09%09sink.next(new MemoryStats(gcInfo.getEndTime()));%0A%09%09%09%09%7D%0A%09%09%09%7D;%0A%09%09%09ManagementFactory.getGarbageCollectorMXBeans()%0A%09%09%09%09%09.forEach(bean -%3E ((NotificationEmitter) bean).addNotificationListener(gcListener, null, null));%0A%09%09%7D).subscribe(STATS_SINK::next);%0A%09%7D%0A%7D

Add MemoryStats.java