Frage

Ich verwende eine Suchbibliothek, die empfiehlt, das Suchhandle-Objekt offen zu halten, da dies dem Abfrage-Cache zugute kommen kann.Im Laufe der Zeit habe ich beobachtet, dass der Cache dazu neigt, sich aufzublähen (einige Hundert Megabyte und immer weiter wächst) und OOMs einsetzen.Es gibt keine Möglichkeit, Beschränkungen für diesen Cache durchzusetzen oder zu planen, wie viel Speicher er verwenden kann.Also habe ich die erhöht Xmx Grenze, aber das ist nur eine vorübergehende Lösung des Problems.

Irgendwann denke ich darüber nach, dieses Objekt zu einem zu machen Referent von java.lang.ref.SoftReference.Wenn dem System also der freie Speicher ausgeht, lässt es das Objekt los und erstellt bei Bedarf ein neues.Dies würde die Geschwindigkeit nach dem Neustart etwas verringern, ist aber eine viel bessere Alternative als das Drücken von OOM.

Das einzige Problem, das ich bei SoftReferences sehe, ist, dass es keine saubere Möglichkeit gibt, ihre Referenten zu finalisieren.In meinem Fall muss ich das Suchhandle schließen, bevor ich es zerstöre, da dem System andernfalls möglicherweise die Dateideskriptoren ausgehen.Natürlich kann ich dieses Handle in ein anderes Objekt einbinden, einen Finalizer darauf schreiben (oder es an eine ReferenceQueue/PhantomReference anhängen) und loslassen.Aber hey, jeder einzelne Artikel auf diesem Planeten rät von der Verwendung von Finalizern ab, und insbesondere von Finalizern zum Freigeben von Dateihandles (z. B. Effektives Java Hrsg.II, Seite 27.).

Daher bin ich etwas verwirrt.Sollte ich all diese Ratschläge sorgfältig ignorieren und weitermachen?Gibt es sonst noch andere praktikable Alternativen?Dank im Voraus.

EDIT Nr. 1:Der folgende Text wurde nach dem Testen von Code hinzugefügt, wie von Tom Hawtin vorgeschlagen.Für mich scheint es, dass entweder der Vorschlag nicht funktioniert oder ich etwas übersehe.Hier ist der Code:

class Bloat {  // just a heap filler really
   private double a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;

   private final int ii;

   public Bloat(final int ii) {
      this.ii = ii;
   }
}

// as recommended by Tom Hawtin
class MyReference<T> extends SoftReference<T> {
   private final T hardRef;

   MyReference(T referent, ReferenceQueue<? super T> q) {
      super(referent, q);
      this.hardRef = referent;
   }
}

//...meanwhile, somewhere in the neighbouring galaxy...
{
   ReferenceQueue<Bloat> rq = new ReferenceQueue<Bloat>();
   Set<SoftReference<Bloat>> set = new HashSet<SoftReference<Bloat>>();
   int i=0;

   while(i<50000) {
//      set.add(new MyReference<Bloat>(new Bloat(i), rq));
      set.add(new SoftReference<Bloat>(new Bloat(i), rq));

//      MyReference<Bloat> polled = (MyReference<Bloat>) rq.poll();
      SoftReference<Bloat> polled = (SoftReference<Bloat>) rq.poll();

      if (polled != null) {
         Bloat polledBloat = polled.get();
         if (polledBloat == null) {
           System.out.println("is null :(");
         } else {
           System.out.println("is not null!");
         }
      }
      i++;
   }
}

Wenn ich das Snippet oben mit ausführe -Xmx10m und SoftReferences (wie im Code oben) bekomme ich jede Menge davon is null :( gedruckt.Aber wenn ich den Code durch ersetze MyReference (Auskommentieren von zwei Zeilen mit MyReference und Auskommentieren von Zeilen mit SoftReference) Ich bekomme immer OOM.

Wie ich aus dem Rat verstanden habe, enthält es eine harte Referenz MyReference sollte das Auftreffen von Gegenständen nicht verhindern ReferenceQueue, Rechts?

War es hilfreich?

Lösung

Toms Antwort die richtige ist, aber der Code, der auf die Frage hinzugefügt worden ist, ist nicht das gleiche wie das, was von Tom vorgeschlagen wurde. Was Tom sieht eher wie dies vorschlägt:

class Bloat {  // just a heap filler really
    public Reader res;
    private double a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;

    private final int ii;

    public Bloat(final int ii, Reader res) {
       this.ii = ii;
       this.res = res;
    }
 }

 // as recommended by Tom Hawtin
 class MySoftBloatReference extends SoftReference<Bloat> {
    public final Reader hardRef;

    MySoftBloatReference(Bloat referent, ReferenceQueue<Bloat> q) {
       super(referent, q);
       this.hardRef = referent.res;
    }
 }

 //...meanwhile, somewhere in the neighbouring galaxy...
 {
    ReferenceQueue<Bloat> rq = new ReferenceQueue<Bloat>();
    Set<SoftReference<Bloat>> set = new HashSet<SoftReference<Bloat>>();
    int i=0;

    while(i<50000) {
        set.add(new MySoftBloatReference(new Bloat(i, new StringReader("test")), rq));

        MySoftBloatReference polled = (MySoftBloatReference) rq.poll();

        if (polled != null) {
            // close the reference that we are holding on to
            try {
                polled.hardRef.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        i++;
    }
}
Hinweis

, dass der große Unterschied ist, dass die harte Referenz auf das Objekt, das geschlossen werden muss. Das umgebende Objekt kann und wird, wird Müll gesammelt, so dass Sie nicht die OOM treffen werden, aber erhalten Sie noch eine Chance, die Referenz zu schließen. Sobald Sie die Schleife verlassen, wird empfohlen, dass auch Müll gesammelt. Natürlich in der realen Welt, würden Sie wahrscheinlich nicht eine öffentliche Instanz Mitglied machen res.

Das heißt, wenn Sie offene Dateireferenzen halten, dann führen Sie eine sehr reale Gefahr von aus die, die laufen, bevor Sie über genügend Arbeitsspeicher ausgeführt. Sie wollen wahrscheinlich auch einen LRU-Cache müssen sicherstellen, dass Sie nicht mehr halten, als klebt Finger in der Luft 500 geöffneten Dateien. Diese können auch vom Typ MyReference sein, so dass sie auch Müll gesammelt werden, wenn es sein muss.

Um ein wenig zu klären, wie MySoftBloatReference arbeitet, die Basisklasse, das heißt Softreference, hält immer noch die Referenz auf das Objekt, das alle des Speichers hogging. Dies ist das Objekt, das Sie brauchen die OOM befreit werden zu verhindern. Wenn jedoch das Objekt befreit, müssen Sie noch die Ressourcen befreien, die die aufblasen verwendet, das heißt, aufblasen unter Verwendung von zwei Typen von Ressourcen, Speicher und einen Datei-Handle, diese beiden Ressourcen befreit werden müssen, oder Sie laufen aus dem einen oder anderen der Ressourcen. Der Softreference behandelt den Druck auf die Speicherressource durch das Objekt zu befreien, aber auch die andere Ressource, die Datei-Handle freigeben müssen. Da aufblasen bereits befreit worden ist, können wir es nicht verwenden die entsprechende Ressource zu befreien, so MySoftBloatReference einen harten Verweis auf die interne Ressource hält, die geschlossen werden muss. Sobald sie darüber informiert wurden, dass die aufblasen befreit wurden, das heißt, wenn die Referenz in der Reference auftaucht, dann kann MySoftBloatReference auch die entsprechende Ressource schließen, durch den harten Hinweis, dass es hat.

EDIT: den Code aktualisiert, so dass sie, wenn sie in eine Klasse geworfen kompiliert. Es verwendet einen String das Konzept zu verdeutlichen, wie Sie den Reader zu schließen, die verwendet wird, um die externe Ressource darstellen, die befreit werden muss. In diesem speziellen Fall, dass Strom zu schließen ist effektiv ein no-op, und so ist nicht erforderlich, aber es zeigt, wie dies zu tun ist, wenn es gebraucht wird.

Andere Tipps

Für eine endliche Anzahl von Ressourcen:Unterklasse SoftReference.Die Soft-Referenz sollte auf das umschließende Objekt zeigen.Eine starke Referenz in der Unterklasse sollte auf die Ressource verweisen, damit sie immer gut erreichbar ist.Beim Durchlesen der ReferenceQueue poll Die Ressource kann geschlossen und aus dem Cache entfernt werden.Der Cache muss korrekt freigegeben werden (falls a SoftReference selbst wird durch Müll gesammelt und kann nicht in eine Warteschlange gestellt werden ReferenceQueue).

Achten Sie darauf, dass Sie nur eine begrenzte Anzahl unveröffentlichter Ressourcen im Cache haben – entfernen Sie alte Einträge (tatsächlich können Sie die Soft-Referenzen mit einem endlichen Cache verwerfen, wenn dies Ihrer Situation entspricht).In der Regel ist die Nicht-Speicherressource wichtiger. In diesem Fall sollte ein LRU-Eviction-Cache ohne exotische Referenzobjekte ausreichend sein.

(Meine Antwort #1000.Gepostet vom London DevDay.)

Ahm.
(Soweit ich weiß) Sie können nicht den Stick von beiden Enden halten. Entweder Sie halten zu Ihrer Information, oder Sie lassen es gehen.
Allerdings ... können Sie auf einige der wichtigsten Informationen halten, die Sie würde es ermöglichen, abzuschließen. Natürlich muss die Schlüsselinformation wesentlich kleiner sein als die „echten Informationen“ und nicht die wirklichen Informationen in seinen erreichbar Objektgraphen haben muss (schwacher Verweis könnten Ihnen dort helfen).
Aufbauend auf dem bestehenden Beispiel (achten Sie auf die wichtigsten Informationen Feld):

public class Test1 {
    static class Bloat {  // just a heap filler really
        private double a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z;

        private final int ii;

        public Bloat(final int ii) {
            this.ii = ii;
        }
    }

    // as recommended by Tom Hawtin
    static class MyReference<T, K> extends SoftReference<T> {
        private final K keyInformation;

        MyReference(T referent, K keyInformation, ReferenceQueue<? super T> q) {
            super(referent, q);
            this.keyInformation = keyInformation;
        }

        public K getKeyInformation() {
            return keyInformation;
        }
    }

    //...meanwhile, somewhere in the neighbouring galaxy...
    public static void main(String[] args) throws InterruptedException {
        ReferenceQueue<Bloat> rq = new ReferenceQueue<Bloat>();
        Set<SoftReference<Bloat>> set = new HashSet<SoftReference<Bloat>>();
        int i = 0;

        while (i < 50000) {
            set.add(new MyReference<Bloat, Integer>(new Bloat(i), i, rq));

            final Reference<? extends Bloat> polled = rq.poll();

            if (polled != null) {
                if (polled instanceof MyReference) {
                    final Object keyInfo = ((MyReference) polled).getKeyInformation();
                    System.out.println("not null, got key info: " + keyInfo + ", finalizing...");
                } else {
                    System.out.println("null, can't finalize.");
                }
                rq.remove();
                System.out.println("removed reference");
            }

Edit:
Ich möchte auf dem erarbeiten „entweder halten Sie Ihre Informationen oder lass es gehen“. Angenommen, Sie hatten einen Weg zu halten, um Ihre Informationen. Das hätte den GC gezwungen, Ihre Daten zu demarkieren, so dass die Daten tatsächlich nur gereinigt werden, nachdem Sie mit ihm fertig sind, in einem zweiten GC-Zyklus. Dies ist möglich - und seine genau das, was finalisieren () ist für. Da Sie festgestellt, dass Sie treten nicht der zweite Zyklus möchten, können Sie Ihre Daten nicht halten (wenn a -> b dann b -!> A). das heißt, Sie müssen loslassen.

Edit2:
Eigentlich wäre ein zweiter Zyklus auftreten - aber für die „Schlüsseldaten“, nicht die „großen aufblasen Daten“. Die tatsächlichen Daten würden auf dem ersten Zyklus gelöscht werden.

Edit3:
Offensichtlich würde die wirkliche Lösung einen separaten Thread verwenden, um von der Referenz Warteschlange zu entfernen (nicht poll (), () entfernen, auf dem dedizierten Thread blockiert).

@ Paul - vielen Dank für die Antwort und Klärung.

@Ran - Ich denke, in Ihrem aktuellen Code i ++ ist am Ende der Schleife fehlt. Auch brauchen Sie nicht rq.remove () in der Schleife als rq.poll zu tun () bereits Top-Referenz entfernt, nicht wahr?

Ein paar Punkte:

1) Ich hatte Thread.sleep (1) Erklärung hinzuzufügen, nachdem ich in der Schleife ++ (für beiden Lösungen von Paul und RAN) OOM zu vermeiden, aber das ist irrelevant für das große Bild und ist auch plattformabhängig. Meine Maschine hat einen Quad-Core-CPU und läuft Sun Linux 1.6.0_16 JDK.

2) Nachdem bei diesen Lösungen suchte ich denke, ich werde mit Finalizers bleiben. Blochs Buch bietet folgende Gründe:

  • gibt es keine Garantie Finalizers sofort ausgeführt wird, also nie in einem Finalizerthread kritisch etwas Zeit tun - noch gibt es keine Garantien für SoftRererences
  • hängt nie auf einem Finalizerthread kritischen dauerhaften Zustand zu aktualisieren - Ich bin nicht
  • gibt es eine schweren Leistungseinbußen Finalizers für die Verwendung - In meinem schlimmsten Fall würde ich über ein einzelnes Objekt pro Minute werden Finalisierung oder so. Ich glaube, ich kann damit leben.
  • verwenden try / finally - oh ja, ich werde auf jeden Fall

Notwendigkeit Mit enormer Menge an Gerüst zu erstellen, für das, was scheint eine einfache Aufgabe, mir nicht vernünftig aussieht. Ich meine, buchstäblich, würde die WTF pro Minute Rate ganz für jemand anderes hoch sein, um einen solchen Code suchen.

3) Saddly, gibt es keine Möglichkeit, Punkte zwischen Paul, Tom aufzuspalten und Ran :( Ich hoffe, Tom hätte nichts dagegen, als er schon viele von ihnen bekam :) urteilen zwischen Paul und Ran war viel schwieriger - ich denke, beide Antworten arbeiten und richtig sind. Ich stelle nur Flagge zu Paul Antwort akzeptieren, weil es wurde höher bewertet (und hat ausführlichere Erklärung), aber Ran-Lösung ist überhaupt nicht schlecht und würde wahrscheinlich meine Wahl sein, wenn ich es mit SoftReferences implementieren würde gewählt haben. Danke Jungs!

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top