2013-06-05 5 views
5

La mia applicazione registra l'utilizzo di determinati oggetti - la mia configurazione utilizza AspectJ per identificare i contesti che mi interessano e registra questi usi. Successivamente caricherò i file di log per l'analisi, ma per motivi di efficienza è utile sapere quando un oggetto non è più raggiungibile.Registrazione quando gli oggetti sono dati inutili raccolti

Il mio attuale approccio consiste nel registrare gli oggetti a cui sono interessato con un 'garbage logger', che crea quindi un oggetto 'saver' contenente il codice hash dell'identità dell'oggetto e lo memorizza in una mappa hash debole. L'idea è che quando l'oggetto viene raccolto, l'oggetto saver verrà rimosso dalla mappa hash debole e raccolto, eseguendo così il codice per registrare il codice hash dell'identità dell'oggetto raccolto. Io uso un thread e una coda separati per evitare di causare un collo di bottiglia nel garbage collector. Ecco il codice spazzatura logger:

public class GarbageLogger extends Thread { 

    private final Map<Object,Saver> Savings = 
     Collections.synchronizedMap(new WeakIdentityHashMap<Object,Saver>()); 
    private final ConcurrentLinkedQueue<Integer> clearTheseHash = 
     new ConcurrentLinkedQueue<Integer>(); 

    public void register(Object o){ 
     Savings.put(o,new Saver(System.identityHashCode(o)); 
    } 

    private class Saver{ 
     public Saver(int hash){ this.hash=hash;} 
     private final int hash; 
     @Override 
     public void finalize(){ 
      clearTheseHash.add(hash); 
     } 
    } 

    @Override 
    public void run(){ 

     while(running){   
      if((clearTheseHash.peek() !=null)){ 
       int h = clearTheseHash.poll(); 
       log(h); 
      } 
      else sleep(100); 
     } 
    } 

    // logging and start/end code omitted 
} 

mio problema è che questo sembra molto contorta e, perché deboli mappa di hash non necessariamente cancellare le sue voci a meno che non è necessario spazio, potrebbe essere in attesa di un lungo periodo di tempo dopo l'oggetto è raccolto prima di registrarlo. Fondamentalmente, sto cercando un modo migliore per raggiungere questo obiettivo.

Nota: sto monitorando oggetti arbitrari e non ho alcun controllo sulla loro creazione, quindi non posso sovrascrivere i loro metodi di finalizzazione.

+0

Sembra che quello che vuoi veramente sia una [coda di riferimento] (http://stackoverflow.com/a/14450693/869736). –

+0

Quindi sostituirò WeakIdentityHashMap e ConcurrentLinkedQueue con un ReferenceQueue, ma sarebbe comunque necessario il thread GarbageLogger per inserire i riferimenti raccolti e registrarli? – selig

+0

Questo è corretto. –

risposta

6

Il modo tradizionale per rispondere agli eventi di raccolta dei rifiuti è quello di registrare i WeakReference s con un ReferenceQueue, in cui faranno automaticamente accodati quando l'oggetto si fa riferimento è GC'd, e quindi il polling periodicamente il ReferenceQueue (possibilmente in un thread separato) per fare le pulizie.

Un trucco standard è quello di estendere la classe WeakReference, allegando eventuali informazioni aggiuntive che si desidera sapere quando si verifica la pulizia, e quindi di gettare le WeakReference oggetti nel ReferenceQueue di nuovo al vostro MyWeakReference per ottenere le informazioni.

3

Un'alternativa un po 'più semplice che potrebbe dare risultati più rapidi (e che allevia anche il potenziale collo di bottiglia sul Savings) è

private final ConcurrentMap<WeakReference, Integer> savings = new ConcurrentHashMap<>(); 

public void run() { 
    while(running) { 
     for(WeakReference reference : savings.keySet()) { 
      if(reference.get() == null) { 
       log(savings.remove(reference)); 
      } 
     } 
     sleep(1000); 
    } 
} 

Il rovescio della medaglia è che si deve continuamente scorrere la mappa, al fine di trovare riferimenti nulla osta, ma il vantaggio è un'implementazione più semplice, e reference.get() == null sarà vero non appena l'oggetto viene cancellato (mentre potrebbe esserci un ritardo nella registrazione di un oggetto cancellato in un WeakHashMap). L'utilizzo di un ConcurrentMap riduce il collo di bottiglia che può essere creato dall'uso di un Collections.synchronizedMap e, cosa più importante, impedisce che il ciclo for-each lanci uno ConcurrentModificationException.

+0

Sì, il collo di bottiglia su una mappa sincronizzata sarebbe probabilmente un problema. Pensi che la soluzione di cui sopra (in un commento) con una coda di riferimento sarebbe anche un collo di bottiglia? ... Suppongo che dovrei sincronizzarmi aggiungendo questo. – selig

+0

+1 per evidenziare il problema del collo di bottiglia. – selig

+1

@selig La coda di riferimento non presenta un collo di bottiglia. [Questo codice] (http://java.dzone.com/articles/letting-garbage-collector-do-c) potrebbe aiutarti - combina una coda di riferimento con una mappa concorrente –