Ist es möglich, die Speicherbereinigung in Java zu erzwingen, auch wenn dies schwierig ist? Ich weiß von System.gc();
und Runtime.gc();
aber sie schlagen nur vor, GC zu machen. Wie kann ich GC erzwingen?
Ist es möglich, die Speicherbereinigung in Java zu erzwingen, auch wenn dies schwierig ist? Ich weiß von System.gc();
und Runtime.gc();
aber sie schlagen nur vor, GC zu machen. Wie kann ich GC erzwingen?
Antworten:
Am besten rufen Sie an, System.gc()
was lediglich ein Hinweis für den Garbage Collector ist, dass er eine Sammlung durchführen soll. Es gibt jedoch keine Möglichkeit, eine sofortige Sammlung zu erzwingen, da der Garbage Collector nicht deterministisch ist.
non-deterministic == trouble
GC.Collect()
nicht. In Java gc()
tut.
Die jlibs-Bibliothek verfügt über eine gute Dienstprogrammklasse für die Speicherbereinigung . Mit WeakReference- Objekten können Sie die Speicherbereinigung mit einem kleinen Trick erzwingen .
RuntimeUtil.gc () aus den jlibs:
/**
* This method guarantees that garbage collection is
* done unlike <code>{@link System#gc()}</code>
*/
public static void gc() {
Object obj = new Object();
WeakReference ref = new WeakReference<Object>(obj);
obj = null;
while(ref.get() != null) {
System.gc();
}
}
PhantomReference
mit a zu verwenden, ReferenceQueue
und dann würden Sie nach der Finalisierung, aber noch vor der Bereinigung benachrichtigt. Selbst wenn Sie erfolgreich feststellen würden, dass der Speicher für dieses Objekt zurückgefordert wurde, würde dies in einem Generations-GC wie dem von HotSpot immer noch sehr wenig bedeuten. Normalerweise würde es mit der Säuberung der jungen Generation zusammenfallen.
System.gc(); System.gc();
, aber es wäre sicher interessant zu wissen, ob es jemals besser funktioniert hat. In der Tat System.gc()
würde es ausreichen , nur zu drucken, wie oft es aufgerufen wurde . Die Chance, jemals 2 zu erreichen, ist ziemlich gering.
Der beste (wenn nicht nur) Weg, einen GC zu erzwingen, wäre das Schreiben einer benutzerdefinierten JVM. Ich glaube, die Garbage Collectors sind steckbar, sodass Sie wahrscheinlich nur eine der verfügbaren Implementierungen auswählen und optimieren können.
Hinweis: Dies ist keine einfache Antwort.
Verwenden der Java ™ Virtual Machine Tool-Schnittstelle (JVM TI) die Funktion
jvmtiError ForceGarbageCollection(jvmtiEnv* env)
wird "Die VM zwingen, eine Garbage Collection durchzuführen." Die JVM TI ist Teil der JavaTM Platform Debugger Architecture (JPDA) .
JA, es ist fast möglich zu erzwingen, dass Sie Methoden in derselben Reihenfolge aufrufen müssen, und gleichzeitig sind dies:
System.gc ();
System.runFinalization ();
Selbst wenn es sich nur um ein Objekt handelt, um die Verwendung dieser beiden Methoden gleichzeitig zu bereinigen, wird der Garbage Collector gezwungen, die finalise()
Methode des nicht erreichbaren Objekts zu verwenden, um den zugewiesenen Speicher freizugeben und die Anweisungen der finalize()
Methode auszuführen.
JEDOCH es eine schreckliche Praxis ist die Garbage Collector zu verwenden , da die Verwendung von ihm könnte eine Überlast auf die Software einzuführen, die noch schlimmer als auf dem Speicher sein können, hat der Garbage Collector seinen eigenen Thread , die nicht möglich ist , zu steuern und je nach Der vom GC verwendete Algorithmus kann mehr Zeit in Anspruch nehmen und wird als sehr ineffizient angesehen. Sie sollten Ihre Software mit Hilfe des GC überprüfen, wenn er am schlimmsten ist, da er definitiv kaputt ist. Eine gute Lösung darf nicht vom GC abhängen.
HINWEIS: Nur um zu bedenken, dass dies nur funktioniert, wenn bei der Finalisierungsmethode keine Neuzuweisung des Objekts erfolgt. In diesem Fall bleibt das Objekt am Leben und es wird eine technisch mögliche Auferstehung auftreten.
gc()
ist dies nur ein Hinweis zum Ausführen einer Garbage Collection. runFinalizers()
führt Finalizer nur für Objekte aus, "die als verworfen befunden wurden". Wenn der GC nicht tatsächlich ausgeführt wurde, gibt es möglicherweise keine solchen Objekte ...
In der Dokumentation zu OutOfMemoryError wird angegeben , dass es nicht ausgelöst wird, es sei denn, die VM konnte nach einer vollständigen Speicherbereinigung keinen Speicher zurückfordern. Wenn Sie also weiterhin Speicher zuweisen, bis Sie den Fehler erhalten, haben Sie bereits eine vollständige Speicherbereinigung erzwungen.
Vermutlich war die Frage, die Sie wirklich stellen wollten, "Wie kann ich die Erinnerung zurückgewinnen, von der ich denke, dass ich sie durch Speicherbereinigung zurückgewinnen sollte?"
So fordern Sie GC manuell an (nicht von System.gc ()):
.gc ist ein Kandidat für die Eliminierung in zukünftigen Versionen - ein Sun Engineer hat einmal kommentiert, dass vielleicht weniger als zwanzig Menschen auf der Welt tatsächlich wissen, wie man .gc () verwendet - ich habe letzte Nacht einige Stunden an einer zentralen / kritischen Stelle gearbeitet Datenstruktur unter Verwendung von SecureRandom-generierten Daten, bei etwas mehr als 40.000 Objekten würde die VM langsamer werden, als ob ihr die Zeiger ausgegangen wären. Offensichtlich verschluckte es sich an 16-Bit-Zeigertabellen und zeigte ein klassisches Verhalten bei "fehlerhaften Maschinen".
Ich versuchte -Xms und so weiter, drehte weiter, bis es ungefähr 57, xxx etwas werden würde. Dann würde es gc von etwa 57.127 auf 57.128 nach einem gc () laufen lassen - ungefähr so schnell wie das Code-Bloat im Camp Easy Money.
Ihr Design muss grundlegend überarbeitet werden, wahrscheinlich mit einem Schiebefenster.
Sie können einen GC über die Befehlszeile auslösen. Dies ist nützlich für Batch / Crontab:
jdk1.7.0/bin/jcmd <pid> GC.run
Sehen :
Die JVM-Spezifikation sagt nichts Spezifisches über die Speicherbereinigung aus. Aus diesem Grund steht es den Anbietern frei, GC auf ihre Weise zu implementieren.
Diese Unbestimmtheit führt also zu Unsicherheit im Verhalten der Speicherbereinigung. Sie sollten Ihre JVM-Details überprüfen, um Informationen zu den Garbage Collection-Ansätzen / -Algorithmen zu erhalten. Es gibt auch Optionen zum Anpassen des Verhaltens.
Wenn Sie die Speicherbereinigung erzwingen müssen, sollten Sie möglicherweise überlegen, wie Sie Ressourcen verwalten. Erstellen Sie große Objekte, die im Speicher verbleiben? Erstellen Sie große Objekte (z. B. Grafikklassen) mit einer Disposable
Schnittstelle und rufen Sie nicht auf, dispose()
wenn Sie damit fertig sind? Erklären Sie etwas auf Klassenebene, das Sie nur innerhalb einer einzigen Methode benötigen?
Es wäre besser, wenn Sie den Grund beschreiben würden, warum Sie eine Speicherbereinigung benötigen. Wenn Sie SWT verwenden, können Sie Ressourcen wie Image
und Font
zur Speicherfreigabe bereitstellen. Zum Beispiel:
Image img = new Image(Display.getDefault(), 16, 16);
img.dispose();
Es gibt auch Tools zum Ermitteln nicht verfügbarer Ressourcen.
Wenn Ihnen der Speicher ausgeht und OutOfMemoryException
Sie einen Speicherplatz erhalten, können Sie versuchen, den für Java verfügbaren Heap-Speicherplatz zu erhöhen, indem Sie Ihr Programm mit java -Xms128m -Xmx512m
statt nur starten java
. Dies gibt Ihnen eine anfängliche Heap-Größe von 128 MB und ein Maximum von 512 MB, was weit mehr als die Standardgröße von 32 MB / 128 MB ist.
java -Xms512M -Xmx1024M
Eine andere Möglichkeit besteht darin, keine neuen Objekte zu erstellen.
Objektpooling ist weg, um den GC-Bedarf in Java zu verringern.
Das Objekt-Pooling ist im Allgemeinen nicht schneller als die Objekterstellung (insbesondere für Lightweight-Objekte), aber schneller als die Garbage Collection. Wenn Sie 10.000 Objekte erstellt haben und jedes Objekt 16 Byte groß war. Das sind 160.000 Bytes, die GC zurückfordern muss. Wenn Sie jedoch nicht alle 10.000 gleichzeitig benötigen, können Sie einen Pool zum Recyceln / Wiederverwenden der Objekte erstellen, sodass keine neuen Objekte erstellt werden müssen und keine alten Objekte mehr analysiert werden müssen.
So etwas (ungetestet). Und wenn Sie möchten, dass es threadsicher ist, können Sie die LinkedList gegen eine ConcurrentLinkedQueue austauschen.
public abstract class Pool<T> {
private int mApproximateSize;
private LinkedList<T> mPool = new LinkedList<>();
public Pool(int approximateSize) {
mApproximateSize = approximateSize;
}
public T attain() {
T item = mPool.poll();
if (item == null) {
item = newInstance();
}
return item;
}
public void release(T item) {
int approxSize = mPool.size(); // not guaranteed accurate
if (approxSize < mApproximateSize) {
recycle(item);
mPool.add(item);
} else if (approxSize > mApproximateSize) {
decommission(mPool.poll());
}
}
public abstract T newInstance();
public abstract void recycle(T item);
public void decommission(T item) { }
}
Unter OracleJDK 10 mit G1 GC führt ein einziger Aufruf von System.gc()
dazu, dass GC die alte Sammlung bereinigt. Ich bin nicht sicher, ob GC sofort ausgeführt wird. GC bereinigt die Young Collection jedoch nicht, selbst wenn System.gc()
sie mehrmals in einer Schleife aufgerufen wird. Damit GC die Young Collection bereinigt, müssen Sie in einer Schleife (z . B. new byte[1024]
) ohne Aufruf zuweisen System.gc()
. Wenn Sie System.gc()
aus irgendeinem Grund anrufen, kann GC die Young Collection nicht bereinigen.
Wirklich, ich verstehe dich nicht. Aber um klar zu sein über "Unendliche Objekterstellung", meinte ich, dass es in meinem großen System einen Teil des Codes gibt, der Objekte erstellt, die im Speicher verarbeitet und lebendig sind. Ich konnte diesen Teil des Codes eigentlich nicht bekommen, nur eine Geste !!
Das ist richtig, nur Geste. Sie haben so ziemlich die Standardantworten, die bereits von mehreren Postern gegeben wurden. Nehmen wir dies eins nach dem anderen:
Richtig, es gibt kein tatsächliches JVM - dies ist nur eine Spezifikation, eine Menge Informatik, die ein gewünschtes Verhalten beschreibt ... Ich habe mich kürzlich mit der Initialisierung von Java-Objekten aus nativem Code befasst. Um das zu bekommen, was Sie wollen, müssen Sie nur das tun, was als aggressives Nullen bezeichnet wird. Die Fehler, wenn sie falsch gemacht werden, sind so schlimm, dass wir uns auf den ursprünglichen Umfang der Frage beschränken müssen:
Bei den meisten Postern hier wird davon ausgegangen, dass Sie an einer Schnittstelle arbeiten. In diesem Fall müssten wir prüfen, ob Ihnen jeweils das gesamte Objekt oder ein Objekt übergeben wird.
Wenn Sie kein Objekt mehr benötigen, können Sie dem Objekt null zuweisen. Wenn Sie es jedoch falsch verstehen, wird eine Nullzeigerausnahme generiert. Ich wette, Sie können eine bessere Arbeit erzielen, wenn Sie NIO verwenden
Jedes Mal, wenn Sie, ich oder jemand anderes sagt: " Bitte, ich brauche das fürchterlich. " Es ist fast ein universeller Vorläufer für die fast vollständige Zerstörung dessen, woran Sie arbeiten möchten tatsächlich verwendeter Code und zeigen Sie uns Ihre Frage.
Sei nicht frustriert. Oft führt dies dazu, dass Ihre Datenbank ein Paket verwendet, das irgendwo gekauft wurde, und das ursprüngliche Design nicht für massive Datenstrukturen optimiert ist.
Das ist sehr häufig.
Zu Ihrer Information
Der Methodenaufruf System.runFinalizersOnExit (true) garantiert, dass Finalizer-Methoden aufgerufen werden, bevor Java heruntergefahren wird. Diese Methode ist jedoch von Natur aus unsicher und veraltet. Eine Alternative besteht darin, mit der Methode Runtime.addShutdownHook "Shutdown Hooks" hinzuzufügen.
Masarrat Siddiqui
Es gibt eine indirekte Möglichkeit, den Garbage Collector zu erzwingen. Sie müssen den Heap nur mit temporären Objekten füllen, bis der Garbage Collector ausgeführt wird. Ich habe eine Klasse erstellt, die den Garbage Collector auf diese Weise zwingt:
class GarbageCollectorManager {
private static boolean collectionWasForced;
private static int refCounter = 0;
public GarbageCollectorManager() {
refCounter++;
}
@Override
protected void finalize() {
try {
collectionWasForced = true;
refCounter--;
super.finalize();
} catch (Throwable ex) {
Logger.getLogger(GarbageCollectorManager.class.getName()).log(Level.SEVERE, null, ex);
}
}
public int forceGarbageCollection() {
final int TEMPORARY_ARRAY_SIZE_FOR_GC = 200_000;
int iterationsUntilCollected = 0;
collectionWasForced = false;
if (refCounter < 2)
new GarbageCollectorManager();
while (!collectionWasForced) {
iterationsUntilCollected++;
int[] arr = new int[TEMPORARY_ARRAY_SIZE_FOR_GC];
arr = null;
}
return iterationsUntilCollected;
}
}
Verwendung:
GarbageCollectorManager manager = new GarbageCollectorManager();
int iterationsUntilGcExecuted = manager.forceGarbageCollection();
Ich weiß nicht , wie viel diese Methode ist nützlich, weil es Haufen ständig füllt, aber wenn Sie unternehmenskritische Anwendung, die MUST GC zwingen - wenn dies kann die Java tragbare Weg zu GC zu erzwingen.
Ich möchte hier etwas hinzufügen. Bitte beachten Sie, dass Java nicht auf einer virtuellen Maschine und nicht auf einer tatsächlichen Maschine ausgeführt wird. Die virtuelle Maschine hat ihre eigene Art der Kommunikation mit der Maschine. Es kann von System zu System variieren. Wenn wir jetzt den GC aufrufen, bitten wir die virtuelle Maschine von Java, den Garbage Collector aufzurufen.
Da sich der Garbage Collector in der virtuellen Maschine befindet, können wir ihn nicht zwingen, dort und dann eine Bereinigung durchzuführen. Vielmehr stellen wir unsere Anfrage beim Garbage Collector in die Warteschlange. Dies hängt von der virtuellen Maschine ab. Nach einer bestimmten Zeit (dies kann sich von System zu System ändern, im Allgemeinen, wenn der der JVM zugewiesene Schwellenwertspeicher voll ist) gibt die tatsächliche Maschine den Speicherplatz frei. : D.
Der folgende Code stammt aus der Methode assertGC (...). Es versucht, den nichtdeterministischen Garbage Collector zum Sammeln zu zwingen.
List<byte[]> alloc = new ArrayList<byte[]>();
int size = 100000;
for (int i = 0; i < 50; i++) {
if (ref.get() == null) {
// Test succeeded! Week referenced object has been cleared by gc.
return;
}
try {
System.gc();
} catch (OutOfMemoryError error) {
// OK
}
try {
System.runFinalization();
} catch (OutOfMemoryError error) {
// OK
}
// Approach the jvm maximal allocatable memory threshold
try {
// Allocates memory.
alloc.add(new byte[size]);
// The amount of allocated memory is increased for the next iteration.
size = (int)(((double)size) * 1.3);
} catch (OutOfMemoryError error) {
// The amount of allocated memory is decreased for the next iteration.
size = size / 2;
}
try {
if (i % 3 == 0) Thread.sleep(321);
} catch (InterruptedException t) {
// ignore
}
}
// Test failed!
// Free resources required for testing
alloc = null;
// Try to find out who holds the reference.
String str = null;
try {
str = findRefsFromRoot(ref.get(), rootsHint);
} catch (Exception e) {
throw new AssertionFailedErrorException(e);
} catch (OutOfMemoryError err) {
// OK
}
fail(text + ":\n" + str);
Quelle (aus Gründen der Übersichtlichkeit habe ich einige Kommentare hinzugefügt): NbTestCase-Beispiel
Sie können versuchen, Runtime.getRuntime().gc()
die Dienstprogrammmethode zu verwenden oder zu verwenden. System.gc()
Hinweis: Diese Methoden gewährleisten keine GC. Und ihr Umfang sollte auf JVM beschränkt sein, anstatt sie programmgesteuert in Ihrer Anwendung zu behandeln.