2012-02-07 6 views
8

空きメモリが一定のしきい値に達すると自動的にJavaアプリケーションにメモリ警告を発するための洗練された方法はありますか?メモリが低下しているときにJavaアプリケーションに自動的に警告するにはどうすればよいでしょうか?

これはJeopardyスタイルの質問ですが、私は既に回答があります。ソリューションが私を束にしてくれたので、世界中に発見してほしいと思っています。

+0

も参照してください他の質問/回答は、古い "Javaの専門家" の問題でそれを見つけました質問/ 433406/get-the-get-the-max-sizes-of-heap-and-permgen-from-jvm – DNA

答えて

8

Heinz Kabutzによって書かれたすばらしいクラスで、完璧に私のために「すぐに」使えます。 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"); 
    } 
} 

使用法:等http://stackoverflow.com/として `MemoryMXBean`に言及

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); 
     } 
    }); 
+1

これは、GCの後に十分な空き領域がある場合でも発生します。バックグラウンドスレッドは、GCの直後にのみチェックする方が便利です。 –

+1

これは、テナントスペースがサイズを変える可能性があるという問題があります。これは、warningThresholdが時間の経過と共に不正確になる可能性があることを意味します。あなたのテニュアされたスペースが大きく断片化されている場合、実際には使用できない空き領域が十分にあるように見えることがあります。つまり、50%満杯にしか見えない場合はOOMEを取得できます。 ;) –

関連する問題