package com.mobilesorcery.sdk.core.memory;
import javax.management.*;
import com.mobilesorcery.sdk.core.CoreMoSyncPlugin;
import java.lang.management.*;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Based on an older Java Specialists issue:
* http://www.javaspecialists.eu/archive/Issue092.html
*
* Original code written by Dr. Heinz M. Kabutz.
*/
public class LowMemoryManager implements NotificationListener {
private final CopyOnWriteArrayList<MemoryLowListener> listeners = new CopyOnWriteArrayList<MemoryLowListener>();
private final IdentityHashMap<MemoryLowListener, Integer> notificationCounts = new IdentityHashMap<MemoryLowListener, Integer>();
private final double thresholdPercentage;
private boolean active = false;
public LowMemoryManager(double thresholdPercentage) {
this.thresholdPercentage = thresholdPercentage;
addDefaultMonitoredPools();
setPercentageUsageThreshold(thresholdPercentage);
}
public void addMemoryLowListener(MemoryLowListener listener, int maxNotificationCount) {
if (listeners.isEmpty()) {
activate();
}
if (!listeners.contains(listener)) {
listeners.add(listener);
}
notificationCounts.put(listener, maxNotificationCount == 0 ? Integer.MAX_VALUE : maxNotificationCount);
}
public void removeMemoryLowListener(MemoryLowListener listener) {
listeners.remove(listener);
notificationCounts.remove(listener);
if (listeners.isEmpty()) {
deactivate();
}
}
private void activate() {
MemoryMXBean mbean = ManagementFactory.getMemoryMXBean();
NotificationEmitter emitter = (NotificationEmitter) mbean;
emitter.addNotificationListener(this, null, null);
active = true;
}
private void deactivate() {
if (active) {
MemoryMXBean mbean = ManagementFactory.getMemoryMXBean();
NotificationEmitter emitter = (NotificationEmitter) mbean;
try {
emitter.removeNotificationListener(this);
} catch (ListenerNotFoundException e) {
// Ignore & log
CoreMoSyncPlugin.getDefault().log(e);
}
}
}
private final ArrayList<MemoryPoolMXBean> monitoredPools = new ArrayList<MemoryPoolMXBean>();
public void setPercentageUsageThreshold(double percentage) {
for (MemoryPoolMXBean monitoredPool : monitoredPools) {
long maxMemory = monitoredPool.getUsage().getMax();
long threshold = (long) (maxMemory * thresholdPercentage);
monitoredPool.setUsageThreshold(threshold);
}
}
/**
* Tenured Space Pool can be determined by it being of type HEAP and by it
* being possible to set the usage threshold.
*/
private void addDefaultMonitoredPools() {
for (MemoryPoolMXBean pool : ManagementFactory.getMemoryPoolMXBeans()) {
if (pool.isUsageThresholdSupported()) {
monitoredPools.add(pool);
if (CoreMoSyncPlugin.getDefault().isDebugging()) {
CoreMoSyncPlugin.trace("Monitoring memory pool {0}", pool.getName());
}
}
}
}
@Override
public synchronized void handleNotification(Notification notification, Object handback) {
if (notification.getType().equals(
MemoryNotificationInfo.MEMORY_THRESHOLD_EXCEEDED)) {
for (MemoryLowListener listener : listeners) {
Integer notificationCount = notificationCounts.get(listener);
int newNotificationCount = notificationCount == null ? 0 : notificationCount - 1;
if (newNotificationCount < 0) {
removeMemoryLowListener(listener);
} else {
notificationCounts.put(listener, newNotificationCount);
listener.memoryUsageLow();
}
}
}
}
public List<MemoryPoolMXBean> getMonitoredPools() {
return Collections.unmodifiableList(monitoredPools);
}
public boolean isUsageExceeded(MemoryPoolMXBean pool) {
return pool.isUsageThresholdSupported() && pool.isUsageThresholdExceeded();
}
}