2013-06-05 13 views
5

Meine Anwendung protokolliert die Verwendung bestimmter Objekte - mein Setup verwendet AspectJ, um die Kontexte zu identifizieren, die mich interessieren, und protokolliert diese Verwendungen. Ich lade später die Protokolldateien zur Analyse, aber aus Effizienzgründen ist es nützlich zu wissen, wann ein Objekt nicht mehr erreichbar ist.Protokollierung, wenn Objekte Müll gesammelt werden

Mein aktueller Ansatz besteht darin, die Objekte, an denen ich interessiert bin, mit einem 'Mülllogger' zu protokollieren, der dann ein 'Sicherheitsobjekt' erstellt, das den Identitätshashcode des Objekts enthält und in einer schwachen Hash-Map speichert. Die Idee ist, dass, wenn das Objekt gesammelt wird, das Sicherungsobjekt aus der schwachen Hash-Map entfernt und gesammelt wird, wodurch Code ausgeführt wird, um den Identitäts-Hash-Code des gesammelten Objekts zu protokollieren. Ich verwende einen separaten Thread und eine Warteschlange, um einen Flaschenhals im Garbage Collector zu vermeiden. Hier ist der Müll-Logger-Code:

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 
} 

Mein Problem ist, dass dies scheint sehr verworren und, weil schwache Hash-Karte wird nicht unbedingt die Einträge löschen, es sei denn Platz benötigt wird, könnte ich eine lange Zeit warten, nachdem das Objekt vor der Aufnahme gesammelt. Im Grunde suche ich nach einem besseren Weg, dies zu erreichen.

Hinweis - Ich überwache willkürliche Objekte und habe keine Kontrolle über ihre Erstellung, so dass sie ihre Finalisierungsmethoden nicht überschreiben können.

+0

Es klingt wie, was Sie wirklich wollen, ist eine [Reference Queue] (http:// Stackoverflow.com/a/14450693/869736). –

+0

Also würde ich die WeakIdentityHashMap und ConcurrentLinkedQueue durch eine ReferenceQueue ersetzen, aber würde immer noch den GarbageLogger-Thread benötigen, um gesammelte Referenzen zu veröffentlichen und sie zu protokollieren? – selig

+0

Das ist richtig. –

Antwort

6

Der traditionelle Weg der Garbage Collection Ereignisse zu reagieren, ist die WeakReference s mit einem ReferenceQueue zu registrieren, in dem sie automatisch eine Warteschlange eingereiht werden, werden, wenn das referenzierte Objekt GC'd ist, und dann in regelmäßigen Abständen die ReferenceQueue (möglicherweise abzufragen in ein separater Thread), um Aufräumarbeiten durchzuführen.

Ein Standard-Trick ist, die WeakReference Klasse zu erweitern, zusätzliche Informationen Befestigen Sie wissen möchten, wenn die Bereinigung erfolgt und dann die WeakReference Objekte zu werfen, um Ihren MyWeakReference im ReferenceQueue zurück, um die Informationen zu erhalten.

3

Eine etwas einfachere Alternative, die schnellere Ergebnisse geben könnte (und das auch bei möglichen Engpässen auf Savings mildert) ist

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

Der Nachteil ist, dass man immer wieder durch die Karte gelöscht, um Referenzen zu finden laufen müssen, aber der Vorteil ist eine einfachere Implementierung, und reference.get() == null wird wahr sein, sobald das Objekt gelöscht wird (während es möglicherweise eine Verzögerung beim Registrieren eines gelöschten Objekts in einem WeakHashMap gibt). Die Verwendung einer ConcurrentMap erleichtert den Engpass, der durch die Verwendung einer Collections.synchronizedMap verursacht werden kann, und verhindert, dass die for-each-Schleife eine ConcurrentModificationException wirft.

+0

Ja, der Engpass auf einer synchronisierten Karte wäre wahrscheinlich ein Problem. Denkst du, dass die obige Lösung (in einem Kommentar) mit einer Referenzwarteschlange auch ein Engpass sein würde? ... Ich vermutete, dass ich mich beim Hinzufügen darauf synchronisieren müsste. – selig

+0

+1 zur Hervorhebung eines Engpassproblems. – selig

+1

@selig Die Referenzwarteschlange würde keinen Flaschenhals darstellen. [Dieser Code] (http://java.dzone.com/articles/letting-garbage-collector-do-c) könnte Ihnen dabei helfen - er kombiniert eine Referenzwarteschlange mit einer gleichzeitigen Karte –

Verwandte Themen