1 package org.simantics;
\r
3 import java.lang.management.ManagementFactory;
\r
4 import java.lang.management.MemoryPoolMXBean;
\r
5 import java.lang.management.MemoryType;
\r
6 import java.util.Collection;
\r
7 import java.util.concurrent.CopyOnWriteArrayList;
\r
8 import java.util.concurrent.ScheduledFuture;
\r
9 import java.util.concurrent.TimeUnit;
\r
12 * This memory warning system will call the listener when we exceed the
\r
13 * percentage of available memory specified. There should only be one instance
\r
14 * of this object created, since the usage threshold can only be set to one
\r
17 * ( adapted from http://www.javaspecialists.eu/archive/Issue092.html )
\r
19 * @author Antti Villberg
\r
21 public class MemoryWarningSystem {
\r
23 public interface MemoryWarningListener {
\r
24 void memoryLow(long usedMemory);
\r
27 private final Collection<MemoryWarningListener> listeners = new CopyOnWriteArrayList<MemoryWarningListener>();
\r
29 private static final MemoryPoolMXBean tenuredGenPool = findTenuredGenPool();
\r
31 private double percentage = 1.0;
\r
33 private ScheduledFuture<?> future;
\r
35 private boolean disposed = false;
\r
37 public MemoryWarningSystem(int amount, TimeUnit unit) {
\r
38 // MemoryMXBean mbean = ManagementFactory.getMemoryMXBean();
\r
39 // NotificationEmitter emitter = (NotificationEmitter) mbean;
\r
40 // emitter.addNotificationListener(new NotificationListener() {
\r
42 // public void handleNotification(Notification n, Object hb) {
\r
43 // if (n.getType().equals(
\r
44 // MemoryNotificationInfo.MEMORY_THRESHOLD_EXCEEDED)) {
\r
46 // long used = getUsed();
\r
47 // for (MemoryWarningListener listener : listeners) {
\r
48 // listener.memoryLow(used);
\r
51 // setPercentageUsageThreshold(percentage);
\r
56 future = Simantics.scheduleAtFixedRate(new Runnable() {
\r
60 if(!disposed && !check()) {
\r
62 for (MemoryWarningListener listener : listeners) {
\r
63 listener.memoryLow(bytes);
\r
68 }, 0, amount, unit);
\r
73 return tenuredGenPool.getUsage().getUsed();
\r
76 public boolean check() {
\r
77 long max = Runtime.getRuntime().maxMemory();
\r
78 long usedMemory = get();
\r
79 long threshold = (long)(max * percentage);
\r
80 return usedMemory < threshold;
\r
83 public long getUsed() {
\r
84 return tenuredGenPool.getUsage().getUsed();
\r
87 public boolean addListener(MemoryWarningListener listener) {
\r
88 return listeners.add(listener);
\r
91 public boolean removeListener(MemoryWarningListener listener) {
\r
92 return listeners.remove(listener);
\r
95 public void setPercentageUsageThreshold(double percentage) {
\r
96 if (percentage <= 0.0 || percentage > 1.0) {
\r
97 throw new IllegalArgumentException("Percentage not in range");
\r
99 this.percentage = percentage;
\r
100 // long maxMemory = Runtime.getRuntime().maxMemory();
\r
101 // long warningThreshold = (long) (maxMemory * percentage);
\r
102 // tenuredGenPool.setUsageThreshold(0);
\r
103 // tenuredGenPool.setUsageThreshold(warningThreshold);
\r
107 * Tenured Space Pool can be determined by it being of type HEAP and by it
\r
108 * being possible to set the usage threshold.
\r
110 private static MemoryPoolMXBean findTenuredGenPool() {
\r
111 for (MemoryPoolMXBean pool : ManagementFactory.getMemoryPoolMXBeans()) {
\r
112 // I don't know whether this approach is better, or whether
\r
113 // we should rather check for the pool name "Tenured Gen"?
\r
114 if (pool.getType() == MemoryType.HEAP
\r
115 && pool.isUsageThresholdSupported()) {
\r
119 throw new IllegalStateException("Could not find tenured space");
\r
122 public void dispose() {
\r
125 future.cancel(false);
\r