Wie kann man Speicher in Java freigeben?

146

Gibt es eine Möglichkeit, Speicher in Java freizugeben, ähnlich wie bei C free()? Oder ist es die einzige Option, das Objekt auf null zu setzen und sich auf GC zu verlassen?

Felix
quelle
151
Ok ... lassen Sie uns eines klarstellen. Nur weil Sie denken, dass etwas eine schlechte Praxis ist und nicht dazu ermutigt, etwas zu tun, ist es nicht wert, abgelehnt zu werden. Dies ist eine klare und gültige Frage, ob es eine Möglichkeit gibt, Speicher in Java freizugeben, ohne sich auf die Speicherbereinigung verlassen zu müssen. Obwohl es entmutigt und im Allgemeinen nicht nützlich oder eine gute Idee sein mag, können Sie nicht wissen, dass es keine Szenarien gibt, in denen es erforderlich sein könnte, ohne zu wissen, was Felix weiß. Felix plant möglicherweise nicht einmal, es zu verwenden. Er möchte vielleicht nur wissen, ob es möglich ist. Es verdient in keiner Weise eine Abstimmung.
Daniel Bingham
7
Zur Verdeutlichung richtet sich dies an denjenigen, der dies abgelehnt hat - nicht unbedingt an vorherige Kommentare.
Daniel Bingham

Antworten:

96

Java verwendet verwalteten Speicher. Sie können also nur mithilfe des newOperators Speicher zuweisen. Die einzige Möglichkeit, Speicher freizugeben, besteht darin, sich auf den Garbage Collector zu verlassen.

Dieses Whitepaper zur Speicherverwaltung (PDF) kann Ihnen helfen, zu erklären, was los ist.

Sie können auch anrufen, System.gc()um vorzuschlagen, dass der Garbage Collector sofort ausgeführt wird. Die Java Runtime trifft jedoch die endgültige Entscheidung, nicht Ihren Code.

Gemäß der Java - Dokumentation ,

Das Aufrufen der gc-Methode legt nahe, dass die Java Virtual Machine Anstrengungen unternimmt, um nicht verwendete Objekte zu recyceln, um den aktuell belegten Speicher für eine schnelle Wiederverwendung verfügbar zu machen. Wenn die Steuerung vom Methodenaufruf zurückkehrt, hat die Java Virtual Machine nach besten Kräften versucht, Speicherplatz von allen verworfenen Objekten zurückzugewinnen.

Daniel Pryden
quelle
5
Es zwingt den Garbage Collector zum Ausführen. Es zwingt es jedoch nicht, Speicher freizugeben ...
Pablo Santa Cruz
13
Nein Pablo, es zwingt den GC nicht zum Laufen.
Jesper
1
Mir wurde von einer sehr zuverlässigen Person gesagt, dass alle Müllsammler von HotSpotVM dies System.gc()völlig ignorieren .
Esko
1
Unter winXp führt Java SE GC jedes System.gc () oder fast jedes aus, aber das API-Dokument garantiert dies nicht.
Teodozjan
2
@Pablo Santa Cruz Was meinst du damit, dass es keinen Speicher frei macht? Ich habe es gerade in meinem Programm getestet, das ein Leck zu haben schien und die RAM-Nutzung schien sich zu stabilisieren. Und Daniel sagte nur, es deutet nur darauf hin, warum sich der Prozentsatz des verwendeten RAM jedes Mal stabilisierte, wenn ich die Methode aufrief. Ihr Leute verwirrt mich.
65

Niemand scheint erwähnt zu haben, dass explizit Objektreferenzen festgelegt werden. Dies nullist eine legitime Technik, um Speicher freizugeben, den Sie möglicherweise in Betracht ziehen möchten.

Angenommen, Sie haben List<String>am Anfang einer Methode, deren Größe sehr groß wurde, eine deklariert , die jedoch nur bis zur Hälfte der Methode erforderlich war. Zu diesem Zeitpunkt können Sie die Listenreferenz so einstellen null, dass der Garbage Collector dieses Objekt möglicherweise zurückfordern kann, bevor die Methode abgeschlossen ist (und die Referenz ohnehin nicht mehr gültig ist).

Beachten Sie, dass ich diese Technik in der Realität selten verwende, aber es lohnt sich, sie in Betracht zu ziehen, wenn Sie mit sehr großen Datenstrukturen arbeiten.

Adamski
quelle
8
Wenn Sie wirklich viel an einem Objekt arbeiten, das nur für einen Teil einer Methode verwendet wird, schlage ich auch vor; Ihre Methode ist zu kompiliert, teilen Sie die Methode in die Vorher- und Nachher-Abschnitte auf oder verwenden Sie einen Block für die erste Hälfte des Codes (
letzterer
5
Der Ort, an dem das Setzen einer Objektreferenz auf null wichtig ist, ist, wenn auf sie von einem anderen langlebigen Objekt (oder möglicherweise von einer statischen Variable) verwiesen wird. Wenn Sie beispielsweise ein langlebiges Array großer Objekte haben und eines dieser Objekte nicht mehr verwenden, sollten Sie die Array-Referenz auf null setzen, um das Objekt für GC verfügbar zu machen.
Hot Licks
22
System.gc(); 

Führt den Garbage Collector aus.

Das Aufrufen der gc-Methode legt nahe, dass die Java Virtual Machine Anstrengungen unternimmt , um nicht verwendete Objekte zu recyceln, um den aktuell belegten Speicher für eine schnelle Wiederverwendung verfügbar zu machen. Wenn die Steuerung vom Methodenaufruf zurückkehrt, hat die Java Virtual Machine nach besten Kräften versucht, Speicherplatz von allen verworfenen Objekten zurückzugewinnen.

Nicht empfohlen.

Bearbeiten: Ich habe die ursprüngliche Antwort im Jahr 2009 geschrieben. Es ist jetzt 2015.

Müllsammler sind in den ~ 20 Jahren, in denen Java existiert, immer besser geworden. Wenn Sie den Garbage Collector manuell aufrufen, sollten Sie an dieser Stelle andere Ansätze in Betracht ziehen:

  • Wenn Sie GC auf eine begrenzte Anzahl von Maschinen sind zu zwingen, kann es sich lohnen, die eine Lastenausgleichspunkt weg von der aktuellen Maschine, warten , dass es dazu dient, angeschlossenen Clients zu beenden, Timeout nach einiger Zeit für Verbindungen hängen, und dann nur schwer -Starten Sie die JVM neu. Dies ist eine schreckliche Lösung, aber wenn Sie sich System.gc () ansehen, können erzwungene Neustarts eine mögliche Notlösung sein.
  • Verwenden Sie einen anderen Garbage Collector. Beispielsweise ist der G1-Kollektor (neu in den letzten sechs Jahren) ein Modell mit geringer Pause. Es verbraucht insgesamt mehr CPU, aber es ist am besten, niemals einen Hard-Stop bei der Ausführung zu erzwingen. Da Server-CPUs mittlerweile fast alle über mehrere Kerne verfügen, ist dies ein wirklich guter Kompromiss.
  • Sehen Sie sich Ihre Flags an, die die Speichernutzung optimieren. Insbesondere in neueren Versionen von Java sollten Sie die Größe von newgen im Heap erhöhen, wenn Sie nicht über so viele langfristig laufende Objekte verfügen. In newgen (jung) werden neue Objekte zugewiesen. Für einen Webserver wird alles, was für eine Anforderung erstellt wurde, hier abgelegt. Wenn dieser Speicherplatz zu klein ist, verbringt Java zusätzliche Zeit damit, die Objekte auf einen längerlebigen Speicher zu aktualisieren, dessen Tötung teurer ist. (Wenn newgen etwas zu klein ist, werden Sie dafür bezahlen.) Zum Beispiel in G1:
    • XX: G1NewSizePercent (standardmäßig 5; spielt wahrscheinlich keine Rolle.)
    • XX: G1MaxNewSizePercent (standardmäßig 60; wahrscheinlich erhöhen.)
  • Sagen Sie dem Müllsammler, dass Sie mit einer längeren Pause nicht einverstanden sind. Dies führt zu häufigeren GC-Läufen, damit das System den Rest seiner Einschränkungen beibehalten kann. In G1:
    • XX: MaxGCPauseMillis (standardmäßig 200.)
Dean J.
quelle
1
Wenn ich meinen eigenen Beitrag kommentiere, macht dies oft nichts, und wenn ich ihn wiederholt aufrufe, kann die JVM instabil werden und so weiter. Es kann auch über Ihren Hund laufen; Gehen Sie vorsichtig vor.
Dean J
1
Ich würde den "Vorschlags" -Teil von "Das Aufrufen der GC-Methode legt nahe, dass die JVM den Aufwand erweitert"
matt b
2
@Jesper, Deans Antwort lautet "schlägt vor". Tatsächlich veröffentlichte er die genaue Dokumentation aus den Javadocs der Methode ...
matt b
2
@ Software Monkey: Ja, ich hätte es einfach bearbeiten können. Aber da Dean J offensichtlich aktiv war (Posting erst vor wenigen Minuten), dachte ich, es wäre eine Höflichkeit, ihn darum zu bitten. Wenn er es nicht getan hätte, wäre ich hierher zurückgekommen und hätte meinen Kommentar bearbeitet und gelöscht.
Daniel Pryden
1
Es lohnt sich auch zu sagen, WARUM es nicht empfohlen wird. Wenn die JVM auf den "Vorschlag" achtet, den GC auszuführen, wird Ihre App mit ziemlicher Sicherheit langsamer, möglicherweise um viele Größenordnungen!
Stephen C
11

* "Ich persönlich verlasse mich auf das Nullstellen von Variablen als Platzhalter für das zukünftige ordnungsgemäße Löschen. Zum Beispiel nehme ich mir die Zeit, alle Elemente eines Arrays zu annullieren, bevor ich das Array selbst lösche (null mache)."

Dies ist nicht erforderlich. Der Java GC funktioniert so, dass er Objekte findet, auf die nicht verwiesen wird. Wenn ich also ein Objekt x mit einer Referenz (= Variable) a habe, die darauf verweist, wird es vom GC nicht gelöscht, da es eine Referenz gibt zu diesem Objekt:

a -> x

Wenn Sie a null setzen, geschieht Folgendes:

a -> null
     x

X hat jetzt keinen Verweis darauf und wird gelöscht. Dasselbe passiert, wenn Sie a so einstellen, dass es auf ein anderes Objekt als x verweist.

Wenn Sie also ein Array arr haben, das auf die Objekte x, y und z verweist, und eine Variable a, die auf das Array verweist, sieht es so aus:

a -> arr -> x
         -> y
         -> z

Wenn Sie a null setzen, geschieht Folgendes:

a -> null
     arr -> x
         -> y
         -> z

Der GC stellt fest, dass arr keinen Verweis darauf hat, und löscht es, wodurch Sie diese Struktur erhalten:

a -> null
     x
     y
     z

Jetzt findet der GC x, y und z und löscht sie ebenfalls. Das Nullstellen jeder Referenz im Array macht nichts besser, es verbraucht nur CPU-Zeit und Speicherplatz im Code (das heißt, es wird nicht weiter schaden. Der GC wird weiterhin in der Lage sein, die Leistung zu erbringen, die er sollte ).

Dakkaron
quelle
5

Ein triftiger Grund für den Wunsch, Speicher von einem Programm (Java oder nicht) freizugeben, besteht darin, anderen Programmen auf Betriebssystemebene mehr Speicher zur Verfügung zu stellen. Wenn meine Java-Anwendung 250 MB verwendet, möchte ich sie möglicherweise auf 1 MB reduzieren und die 249 MB für andere Apps verfügbar machen.

Yios
quelle
Wenn Sie in einem Java-Programm explizit einen Teil von 249 MB freigeben müssen, ist die Speicherverwaltung nicht das erste, an dem ich arbeiten möchte.
Marc DiMillo
3
Durch das Freigeben von Speicher in Ihrem Java-Heap wird der Speicher jedoch (im Allgemeinen) nicht für andere Apps verfügbar.
Hot Licks
5

Um die Antwort und den Kommentar von Yiannis Xanthopoulos und Hot Licks zu erweitern (Entschuldigung, ich kann noch keinen Kommentar abgeben!), Können Sie VM-Optionen wie in diesem Beispiel festlegen:

-XX:+UseG1GC -XX:MinHeapFreeRatio=15 -XX:MaxHeapFreeRatio=30

In meinem JDK 7 wird dann nicht verwendeter VM-Speicher freigegeben, wenn mehr als 30% des Heapspeichers nach dem GC frei werden, wenn die VM inaktiv ist. Sie müssen diese Parameter wahrscheinlich anpassen.

Obwohl ich es im folgenden Link nicht hervorgehoben gesehen habe, beachten Sie, dass einige Garbage Collectors diese Parameter möglicherweise nicht einhalten und Java standardmäßig einen davon für Sie auswählt, falls Sie mehr als einen Kern haben (daher das obige Argument UseG1GC) ).

VM-Argumente

Update: Für Java 1.8.0_73 habe ich gesehen, dass die JVM gelegentlich kleine Mengen mit den Standardeinstellungen veröffentlicht. Scheint dies nur zu tun, wenn ~ 70% des Heaps nicht verwendet werden. Ich weiß nicht, ob es aggressiver wäre, wenn das Betriebssystem nur wenig physischen Speicher hätte.

nsandersen
quelle
4

Ich habe damit experimentiert.

Es ist wahr, dass System.gc();nur vorgeschlagen wird, den Garbage Collector auszuführen.

Wenn Sie jedoch System.gc();nach dem Festlegen aller Verweise auf anrufen null, werden Leistung und Speicherbelegung verbessert.

Hemant Yadav
quelle
Ich denke, Sie können nicht sicher sagen, dass "das Aufrufen von System.gc (); nachdem alle Verweise auf null gesetzt wurden, die Leistung und die Speicherbelegung verbessert". Weil System.gc () eine enorme Rechenkomplexität aufweist. Und selbst nach dem Aufruf von System.gc () und dem Sammeln des Mülls gibt der JVM den Speicher möglicherweise nicht an das Betriebssystem oder System zurück. JVM behält den Speicher möglicherweise zur späteren Bezugnahme bei. Siehe diese Antwort .
Md. Abu Nafee Ibna Zahid
3

Wenn Sie wirklich einen Speicherblock zuweisen und freigeben möchten, können Sie dies mit direkten ByteBuffers tun. Es gibt sogar eine nicht tragbare Möglichkeit, den Speicher freizugeben.

Wie bereits erwähnt, bedeutet dies jedoch keine gute Idee, dies zu tun, nur weil Sie Speicher in C freigeben müssen.

Wenn Sie der Meinung sind, dass Sie wirklich einen guten kostenlosen Anwendungsfall haben (), fügen Sie ihn bitte in die Frage ein, damit wir sehen können, was Sie tun möchten. Es ist sehr wahrscheinlich, dass es einen besseren Weg gibt.

Peter Lawrey
quelle
3

Ganz von javacoffeebreak.com/faq/faq0012.html

Ein Thread mit niedriger Priorität kümmert sich automatisch um die Speicherbereinigung für den Benutzer. Während der Leerlaufzeit kann der Thread aufgerufen werden und er kann beginnen, Speicher freizugeben, der zuvor einem Objekt in Java zugewiesen wurde. Aber keine Sorge - Ihre Objekte werden nicht gelöscht!

Wenn es keine Verweise auf ein Objekt gibt, wird es für den Garbage Collector zu einem fairen Spiel. Anstatt eine Routine aufzurufen (wie in C ++ kostenlos), weisen Sie einfach alle Verweise auf das Objekt null zu oder weisen der Referenz eine neue Klasse zu.

Beispiel:

public static void main(String args[])
{
  // Instantiate a large memory using class
  MyLargeMemoryUsingClass myClass = new MyLargeMemoryUsingClass(8192);

  // Do some work
  for ( .............. )
  {
      // Do some processing on myClass
  }

  // Clear reference to myClass
  myClass = null;

  // Continue processing, safe in the knowledge
  // that the garbage collector will reclaim myClass
}

Wenn Ihr Code eine große Menge an Speicher anfordern soll, möchten Sie möglicherweise den Garbage Collector auffordern, Speicherplatz zurückzugewinnen, anstatt dies als Thread mit niedriger Priorität zuzulassen. Fügen Sie dazu Ihrem Code Folgendes hinzu

System.gc();

Der Garbage Collector versucht, freien Speicherplatz zurückzugewinnen, und Ihre Anwendung kann weiterhin ausgeführt werden, wobei so viel Speicher wie möglich zurückgefordert wird (auf bestimmten Plattformen können Probleme mit der Speicherfragmentierung auftreten).

Anzeigename
quelle
1

In meinem Fall möchte ich, da mein Java-Code in naher Zukunft in andere Sprachen portiert werden soll (hauptsächlich C ++), zumindest ein Lippenbekenntnis zur ordnungsgemäßen Speicherfreigabe ablegen, damit er später den Portierungsprozess erleichtert.

Ich persönlich verlasse mich darauf, Variablen als Platzhalter für das zukünftige ordnungsgemäße Löschen auf Null zu setzen. Zum Beispiel nehme ich mir die Zeit, um alle Elemente eines Arrays zu annullieren, bevor ich das Array selbst lösche (null mache).

Aber mein Fall ist sehr speziell und ich weiß, dass ich dabei Leistungstreffer mache.

Oskuro
quelle
1

* "Angenommen, Sie haben eine Liste am Anfang einer Methode deklariert, deren Größe sehr groß wurde, die jedoch erst nach der Hälfte der Methode erforderlich war. Zu diesem Zeitpunkt können Sie den Listenverweis auf null setzen damit der Garbage Collector dieses Objekt möglicherweise zurückfordern kann, bevor die Methode abgeschlossen ist (und die Referenz ohnehin außerhalb des Gültigkeitsbereichs liegt). " * *

Dies ist korrekt, aber diese Lösung ist möglicherweise nicht verallgemeinerbar. Während Sie einen List-Objektverweis auf null setzen, wird Speicher für die Garbage Collection verfügbar gemacht, gilt dies nur für ein List-Objekt primitiver Typen. Wenn das Listenobjekt stattdessen Referenztypen enthält, wird durch Setzen des Listenobjekts = null keine der in der Liste enthaltenen Referenztypen dereferenziert. In diesem Fall werden durch Festlegen des Listenobjekts = null die enthaltenen Referenztypen verwaist, deren Objekte nicht für die Speicherbereinigung verfügbar sind, es sei denn, der Algorithmus für die Speicherbereinigung ist intelligent genug, um festzustellen, ob die Objekte verwaist sind.

Gothri
quelle
1
Das ist eigentlich nicht wahr. Der Java Garbage Collector ist intelligent genug, um das richtig zu handhaben. Wenn Sie die Liste auf Null setzen (und die Objekte in der Liste keine anderen Verweise darauf haben), kann der GC alle Objekte in der Liste zurückfordern. Es kann sich entscheiden, dies derzeit nicht zu tun, aber es wird sie schließlich zurückfordern. Gleiches gilt für zyklische Referenzen. Grundsätzlich funktioniert der GC so, dass er gezielt nach verwaisten Objekten sucht und diese dann zurückfordert. Dies ist die ganze Aufgabe eines GC. Die Art und Weise, wie Sie es beschreiben, würde einen GC völlig unbrauchbar machen.
Dakkaron
1

Obwohl Java eine automatische Speicherbereinigung bietet, möchten Sie manchmal wissen, wie groß das Objekt ist und wie viel davon noch übrig ist. Freier Speicher wird programmgesteuert verwendet, import java.lang;und Runtime r=Runtime.getRuntime(); um Speicherwerte zu erhalten, indem Sie mem1=r.freeMemory();den freien Speicher verwenden, rufen Sie die r.gc();Methode und den Aufruf auffreeMemory()

Benjamin
quelle
1

Von JAVA wird empfohlen, null zuzuweisen

Von https://docs.oracle.com/cd/E19159-01/819-3681/abebi/index.html

Das explizite Zuweisen eines Nullwerts zu Variablen, die nicht mehr benötigt werden, hilft dem Garbage Collector, die Teile des Speichers zu identifizieren, die sicher zurückgefordert werden können. Java bietet zwar Speicherverwaltung, verhindert jedoch keine Speicherverluste oder die Verwendung übermäßiger Speichermengen.

Eine Anwendung kann Speicherlecks verursachen, indem sie keine Objektreferenzen freigibt. Dies verhindert, dass der Java-Garbage Collector diese Objekte zurückfordert, und führt dazu, dass immer mehr Speicher verwendet wird. Durch das explizite Aufheben von Verweisen auf Variablen nach ihrer Verwendung kann der Garbage Collector Speicher zurückfordern.

Eine Möglichkeit, Speicherlecks zu erkennen, besteht darin, Profiling-Tools zu verwenden und nach jeder Transaktion Speicher-Snapshots zu erstellen. Eine leckfreie Anwendung im stationären Zustand zeigt nach Speicherbereinigungen einen stabilen aktiven Heapspeicher an.

user3869837
quelle