2012-02-07 14 views
8

Esiste un modo elegante per attivare automaticamente gli avvisi di memoria nella mia applicazione Java quando la memoria libera raggiunge una determinata soglia?Qual è un ottimo modo per avvisare automaticamente la mia applicazione Java quando la memoria si sta esaurendo?

Si noti che questa è una domanda in stile Jeopardy a cui ho già una risposta, volevo solo postarla qui per il mondo da scoprire perché la soluzione mi ha aiutato un sacco.

+0

Vedi anche altre domande/risposte che menzionano il 'MemoryMXBean', come http://stackoverflow.com/ domande/433406/how-to-get-the-max-sizes-of-the-heap-and-permgen-from-the-jvm – DNA

risposta

8

Ecco una piccola grande classe scritta da Heinz Kabutz che funziona perfettamente per me "out of the box". Trovato in un problema vecchio "specialisti 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"); 
    } 
} 

utilizzo:

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

Questo sparerà anche se ci sarebbe molto spazio libero dopo un GC. Sarebbe più utile avere un thread in background che controlli subito dopo un GC. –

+1

Un altro problema che questo può avere è che lo spazio di possesso può cambiare dimensione. Ciò significa che warningThreshold può diventare non corretto nel tempo. Se il tuo spazio di gioco è fortemente frammentato, può sembrare che abbia molto spazio libero che non è effettivamente utilizzabile, ovvero puoi ottenere un OOME quando sembri essere pieno solo al 50%. ;) –

Problemi correlati