--- /dev/null
+package org.simantics;\r
+\r
+import java.lang.management.ManagementFactory;\r
+import java.lang.management.MemoryPoolMXBean;\r
+import java.lang.management.MemoryType;\r
+import java.util.Collection;\r
+import java.util.concurrent.CopyOnWriteArrayList;\r
+import java.util.concurrent.ScheduledFuture;\r
+import java.util.concurrent.TimeUnit;\r
+\r
+/**\r
+ * This memory warning system will call the listener when we exceed the\r
+ * percentage of available memory specified. There should only be one instance\r
+ * of this object created, since the usage threshold can only be set to one\r
+ * number.\r
+ * \r
+ * ( adapted from http://www.javaspecialists.eu/archive/Issue092.html )\r
+ * \r
+ * @author Antti Villberg\r
+ */\r
+public class MemoryWarningSystem {\r
+\r
+ public interface MemoryWarningListener {\r
+ void memoryLow(long usedMemory);\r
+ }\r
+\r
+ private final Collection<MemoryWarningListener> listeners = new CopyOnWriteArrayList<MemoryWarningListener>();\r
+\r
+ private static final MemoryPoolMXBean tenuredGenPool = findTenuredGenPool();\r
+\r
+ private double percentage = 1.0;\r
+\r
+ private ScheduledFuture<?> future;\r
+\r
+ private boolean disposed = false;\r
+\r
+ public MemoryWarningSystem(int amount, TimeUnit unit) {\r
+// MemoryMXBean mbean = ManagementFactory.getMemoryMXBean();\r
+// NotificationEmitter emitter = (NotificationEmitter) mbean;\r
+// emitter.addNotificationListener(new NotificationListener() {\r
+// @Override\r
+// public void handleNotification(Notification n, Object hb) {\r
+// if (n.getType().equals(\r
+// MemoryNotificationInfo.MEMORY_THRESHOLD_EXCEEDED)) {\r
+// if(!check()) {\r
+// long used = getUsed();\r
+// for (MemoryWarningListener listener : listeners) {\r
+// listener.memoryLow(used);\r
+// }\r
+// }\r
+// setPercentageUsageThreshold(percentage);\r
+// }\r
+// }\r
+// }, null, null);\r
+\r
+ future = Simantics.scheduleAtFixedRate(new Runnable() {\r
+\r
+ @Override\r
+ public void run() {\r
+ if(!disposed && !check()) {\r
+ long bytes = get();\r
+ for (MemoryWarningListener listener : listeners) {\r
+ listener.memoryLow(bytes);\r
+ }\r
+ }\r
+ }\r
+\r
+ }, 0, amount, unit);\r
+\r
+ }\r
+ \r
+ public long get() {\r
+ return tenuredGenPool.getUsage().getUsed();\r
+ }\r
+ \r
+ public boolean check() {\r
+ long max = Runtime.getRuntime().maxMemory();\r
+ long usedMemory = get();\r
+ long threshold = (long)(max * percentage);\r
+ return usedMemory < threshold;\r
+ }\r
+ \r
+ public long getUsed() {\r
+ return tenuredGenPool.getUsage().getUsed();\r
+ }\r
+\r
+ public boolean addListener(MemoryWarningListener listener) {\r
+ return listeners.add(listener);\r
+ }\r
+\r
+ public boolean removeListener(MemoryWarningListener listener) {\r
+ return listeners.remove(listener);\r
+ }\r
+\r
+ public void setPercentageUsageThreshold(double percentage) {\r
+ if (percentage <= 0.0 || percentage > 1.0) {\r
+ throw new IllegalArgumentException("Percentage not in range");\r
+ }\r
+ this.percentage = percentage;\r
+// long maxMemory = Runtime.getRuntime().maxMemory();\r
+// long warningThreshold = (long) (maxMemory * percentage);\r
+// tenuredGenPool.setUsageThreshold(0);\r
+// tenuredGenPool.setUsageThreshold(warningThreshold);\r
+ }\r
+\r
+ /**\r
+ * Tenured Space Pool can be determined by it being of type HEAP and by it\r
+ * being possible to set the usage threshold.\r
+ */\r
+ private static MemoryPoolMXBean findTenuredGenPool() {\r
+ for (MemoryPoolMXBean pool : ManagementFactory.getMemoryPoolMXBeans()) {\r
+ // I don't know whether this approach is better, or whether\r
+ // we should rather check for the pool name "Tenured Gen"?\r
+ if (pool.getType() == MemoryType.HEAP\r
+ && pool.isUsageThresholdSupported()) {\r
+ return pool;\r
+ }\r
+ }\r
+ throw new IllegalStateException("Could not find tenured space");\r
+ }\r
+\r
+ public void dispose() {\r
+ if (!disposed) {\r
+ disposed = true;\r
+ future.cancel(false);\r
+ listeners.clear();\r
+ }\r
+ }\r
+\r
+}
\ No newline at end of file