9

当可用内存达到某个阈值时,是否有一种优雅的方法可以自动向我的 Java 应用程序发出内存警告?

请注意,这是一个危险风格的问题,我已经有了答案,只是想把它贴在这里让全世界发现,因为这个解决方案帮助了我很多。

4

1 回答 1

9

这是 Heinz Kabutz 写的一堂很棒的小课,它“开箱即用”对我来说完美无缺。在旧的“Java 专家”问题中找到它:http ://www.javaspecialists.eu/archive/Issue092.html

import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryNotificationInfo;
import java.lang.management.MemoryPoolMXBean;
import java.lang.management.MemoryType;
import java.util.ArrayList;
import java.util.Collection;

import javax.management.Notification;
import javax.management.NotificationEmitter;
import javax.management.NotificationListener;

/**
 * This memory warning system will call the listener when we exceed the
 * percentage of available memory specified. There should only be one instance
 * of this object created, since the usage threshold can only be set to one
 * number.
 * 
 * ( adapted from http://www.javaspecialists.eu/archive/Issue092.html )
 */

public class MemoryWarningSystem {

    public interface Listener {

        void memoryUsageLow(long usedMemory, long maxMemory);
    }

    private final Collection<Listener> listeners = new ArrayList<Listener>();

    private static final MemoryPoolMXBean tenuredGenPool = findTenuredGenPool();

    public MemoryWarningSystem() {
        MemoryMXBean mbean = ManagementFactory.getMemoryMXBean();
        NotificationEmitter emitter = (NotificationEmitter) mbean;
        emitter.addNotificationListener(new NotificationListener() {
            @Override
            public void handleNotification(Notification n, Object hb) {
                if (n.getType().equals(
                        MemoryNotificationInfo.MEMORY_THRESHOLD_EXCEEDED)) {
                    long maxMemory = tenuredGenPool.getUsage().getMax();
                    long usedMemory = tenuredGenPool.getUsage().getUsed();
                    for (Listener listener : listeners) {
                        listener.memoryUsageLow(usedMemory, maxMemory);
                    }
                }
            }
        }, null, null);
    }

    public boolean addListener(Listener listener) {
        return listeners.add(listener);
    }

    public boolean removeListener(Listener listener) {
        return listeners.remove(listener);
    }

    public void setPercentageUsageThreshold(double percentage) {
        if (percentage <= 0.0 || percentage > 1.0) {
            throw new IllegalArgumentException("Percentage not in range");
        }
        long maxMemory = tenuredGenPool.getUsage().getMax();
        long warningThreshold = (long) (maxMemory * percentage);
        tenuredGenPool.setUsageThreshold(warningThreshold);
    }

    /**
     * Tenured Space Pool can be determined by it being of type HEAP and by it
     * being possible to set the usage threshold.
     */
    private static MemoryPoolMXBean findTenuredGenPool() {
        for (MemoryPoolMXBean pool : ManagementFactory.getMemoryPoolMXBeans()) {
            // I don't know whether this approach is better, or whether
            // we should rather check for the pool name "Tenured Gen"?
            if (pool.getType() == MemoryType.HEAP
                    && pool.isUsageThresholdSupported()) {
                return pool;
            }
        }
        throw new IllegalStateException("Could not find tenured space");
    }
}

用法:

    MemoryWarningSystem system = new MemoryWarningSystem();
    system.setPercentageUsageThreshold(0.8d);
    system.addListener(new Listener() {
        @Override
        public void memoryUsageLow(long usedMemory, long maxMemory) {
            System.out.println("low: "+usedMemory+" / "+maxMemory);
        }
    });
于 2012-02-07T15:53:04.243 回答