In Java, is there a way to know that a StackOverflow
error or OutOfMemory
exception may happen soon?
The OutOfMemory
exception m
Implementation example using MemoryPoolMXBean
import static java.lang.management.ManagementFactory.getMemoryMXBean;
import static java.lang.management.ManagementFactory.getMemoryPoolMXBeans;
import static java.lang.management.MemoryNotificationInfo.MEMORY_THRESHOLD_EXCEEDED;
import static java.lang.management.MemoryType.HEAP;
import java.lang.management.MemoryPoolMXBean;
import java.lang.management.MemoryUsage;
import java.util.Collection;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.management.Notification;
import javax.management.NotificationEmitter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MemoryWatcher {
public interface Listener {
void memoryUsageLow(long usedMemory, long maxMemory);
}
private static final MemoryPoolMXBean tenuredGenPool = findTenuredGenPool();
private static final Logger logger = LoggerFactory.getLogger(MemoryWatcher.class);
private static MemoryPoolMXBean findTenuredGenPool() {
for (MemoryPoolMXBean pool : getMemoryPoolMXBeans())
if (pool.getType() == HEAP && pool.isUsageThresholdSupported())
return pool;
return null;
}
public static MemoryPoolMXBean getTenuredGenPool() {
return tenuredGenPool;
}
private final Collection listeners = new CopyOnWriteArrayList<>();
public MemoryWatcher(double usageThresholdPercent) {
if (tenuredGenPool == null) {
logger.warn("Tenured pool is not used");
return;
}
if (tenuredGenPool.getUsageThreshold() != 0)
logger.warn("Overriding tenured usage threshold {} with {}", tenuredGenPool.getUsage().getMax() / (double) tenuredGenPool.getUsageThreshold(), usageThresholdPercent);
tenuredGenPool.setUsageThreshold((long) (tenuredGenPool.getUsage().getMax() * usageThresholdPercent));
NotificationEmitter emitter = (NotificationEmitter) getMemoryMXBean();
emitter.addNotificationListener((Notification n, Object hb) -> {
MemoryUsage usage = tenuredGenPool.getUsage();
if (n.getType().equals(MEMORY_THRESHOLD_EXCEEDED) && usage.getMax() == usage.getCommitted())
listeners.forEach(listener -> listener.memoryUsageLow(usage.getUsed(), usage.getMax()));
}, null, null);
}
public boolean addListener(Listener listener) {
return listeners.add(listener);
}
public boolean removeListener(Listener listener) {
return listeners.remove(listener);
}
}