¿Es posible forzar la recolección de basura en Java, incluso si es difícil de hacer? Lo sé System.gc();
y Runtime.gc();
solo sugieren hacer GC. ¿Cómo puedo forzar GC?
¿Es posible forzar la recolección de basura en Java, incluso si es difícil de hacer? Lo sé System.gc();
y Runtime.gc();
solo sugieren hacer GC. ¿Cómo puedo forzar GC?
Respuestas:
Su mejor opción es llamar, lo System.gc()
que simplemente es una pista para el recolector de basura que desea que haga una recolección. Sin embargo, no hay forma de forzar una recolección inmediata ya que el recolector de basura no es determinista.
non-deterministic == trouble
GC.Collect()
no recopila. En Java gc()
sí.
La biblioteca jlibs tiene una buena clase de utilidad para la recolección de basura . Puede forzar la recolección de basura utilizando un ingenioso truco con objetos WeakReference .
RuntimeUtil.gc () de 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
con ay ReferenceQueue
luego recibiría una notificación después de la finalización, pero aún antes de la limpieza. Finalmente, incluso si detecta con éxito que se recuperó la memoria para este objeto, todavía significaría muy poco en un GC generacional como HotSpot's. Por lo general, coincidiría con la limpieza de la generación joven.
System.gc(); System.gc();
, pero seguramente sería interesante saber si alguna vez funcionó mejor que eso. De hecho, solo imprimir cuántas veces fue System.gc()
suficiente. La posibilidad de llegar a 2 es bastante escasa.
La mejor (si no solo) forma de forzar un GC sería escribir una JVM personalizada. Creo que los recolectores de basura son conectables, por lo que probablemente podría elegir una de las implementaciones disponibles y ajustarla.
Nota: Esta NO es una respuesta fácil.
Usando la interfaz Java ™ Virtual Machine Tool (JVM TI) , la función
jvmtiError ForceGarbageCollection(jvmtiEnv* env)
"Obligará a la VM a realizar una recolección de basura". JVM TI es parte de la arquitectura Java Debugger Architecture (JPDA) .
SÍ , es casi posible forzarlo, debe llamar a los métodos en el mismo orden y al mismo tiempo estos son:
System.gc ();
System.runFinalization ();
incluso si es solo un objeto para limpiar el uso de estos dos métodos al mismo tiempo, obligue al recolector de basura a usar el finalise()
método de objeto inalcanzable, liberando la memoria asignada y haciendo lo que dice el finalize()
método.
SIN EMBARGO , es una práctica terrible usar el recolector de basura porque su uso podría introducir una sobrecarga en el software que puede ser incluso peor que en la memoria, el recolector de basura tiene su propio hilo que no es posible controlar más dependiendo de el algoritmo utilizado por el gc podría tomar más tiempo y se considera muy ineficiente, debe verificar su software si es peor con la ayuda del gc porque definitivamente está roto, una buena solución no debe depender del gc.
NOTA: solo para tener en cuenta que esto funcionará solo si en el método de finalización no hay una reasignación del objeto, si esto sucede, el objeto se mantendrá vivo y tendrá una resurrección que es técnicamente posible.
gc()
es solo una pista para ejecutar una recolección de basura. runFinalizers()
solo ejecuta finalizadores en objetos "que han sido descartados". Si el gc no se ejecutó realmente, puede que no haya tales objetos ...
Según la documentación de OutOfMemoryError , declara que no se lanzará a menos que la VM no haya podido recuperar la memoria después de una recolección de basura completa. Entonces, si sigue asignando memoria hasta que obtenga el error, ya habrá forzado una recolección de basura completa.
Presumiblemente, la pregunta que realmente quería hacer era "¿cómo puedo reclamar el recuerdo que creo que debería reclamar mediante la recolección de basura?"
Para solicitar GC manualmente (no desde System.gc ()):
.gc es un candidato para la eliminación en futuras versiones: un ingeniero de Sun comentó una vez que tal vez menos de veinte personas en el mundo realmente saben cómo usar .gc (). Anoche trabajé durante algunas horas en una central estructura de datos que utiliza datos generados por SecureRandom, en algún lugar justo después de 40,000 objetos, el vm se ralentizaría como si se hubiera quedado sin punteros. Claramente, se estaba ahogando en las tablas de punteros de 16 bits y exhibía un comportamiento clásico de "maquinaria defectuosa".
Intenté -Xms y así sucesivamente, seguí jugando un poco hasta que llegó a unos 57, xxx algo. Luego, correría gc pasando de decir 57.127 a 57.128 después de un gc (), aproximadamente al ritmo de hincha de código en el campamento Easy Money.
Su diseño necesita una revisión fundamental, probablemente un enfoque de ventana deslizante.
Puede activar un GC desde la línea de comando. Esto es útil para batch / crontab:
jdk1.7.0/bin/jcmd <pid> GC.run
Ver :
La especificación JVM no dice nada específico sobre la recolección de basura. Debido a esto, los vendedores son libres de implementar GC a su manera.
Entonces, esta vaguedad causa incertidumbre en el comportamiento de recolección de basura. Debe verificar los detalles de su JVM para conocer los enfoques / algoritmos de recolección de basura. También hay opciones para personalizar el comportamiento también.
Si necesita forzar la recolección de basura, tal vez debería considerar cómo administra los recursos. ¿Estás creando objetos grandes que persisten en la memoria? ¿Está creando objetos grandes (p. Ej., Clases de gráficos) que tienen una Disposable
interfaz y no llama dispose()
cuando termina con ella? ¿Estás declarando algo a nivel de clase que solo necesitas dentro de un solo método?
Sería mejor si describiera la razón por la que necesita la recolección de basura. Si está utilizando SWT, puede disponer recursos como Image
y Font
para liberar memoria. Por ejemplo:
Image img = new Image(Display.getDefault(), 16, 16);
img.dispose();
También hay herramientas para determinar recursos no expuestos.
Si se está quedando sin memoria y está obteniendo una OutOfMemoryException
, puede intentar aumentar la cantidad de espacio de almacenamiento dinámico disponible para Java iniciando su programa en java -Xms128m -Xmx512m
lugar de solo java
. Esto le dará un tamaño de almacenamiento dinámico inicial de 128 Mb y un máximo de 512 Mb, que es mucho más que el estándar de 32 Mb / 128 Mb.
java -Xms512M -Xmx1024M
Otra opción es no crear nuevos objetos.
La agrupación de objetos está lejos para reducir la necesidad de GC en Java.
La agrupación de objetos generalmente no va a ser más rápida que la creación de objetos (especialmente para objetos livianos) pero es más rápida que la recolección de basura. Si creó 10,000 objetos y cada objeto tenía 16 bytes. Eso es 160,000 bytes que GC tiene que reclamar. Por otro lado, si no necesita todos los 10,000 al mismo tiempo, puede crear un grupo para reciclar / reutilizar los objetos, lo que elimina la necesidad de construir nuevos objetos y elimina la necesidad de GC objetos antiguos.
Algo como esto (no probado). Y si desea que sea seguro para subprocesos, puede cambiar la LinkedList por una ConcurrentLinkedQueue.
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) { }
}
En OracleJDK 10 con G1 GC, una sola llamada a System.gc()
hará que GC limpie la colección antigua. No estoy seguro si GC se ejecuta de inmediato. Sin embargo, GC no limpiará la Colección Young, incluso si System.gc()
se llama muchas veces en un bucle. Para que GC limpie la Young Collection, debe asignar en un bucle (por ejemplo new byte[1024]
) sin llamar System.gc()
. Llamar System.gc()
por alguna razón evita que GC limpie la Young Collection.
Realmente, no te entiendo. Pero para que quede claro acerca de la "Creación infinita de objetos", quise decir que hay un fragmento de código en mi gran sistema que crea objetos que maneja y está vivo en la memoria, ¡no pude obtener este fragmento de código en realidad, solo un gesto!
Esto es correcto, solo un gesto. Tiene casi las respuestas estándar ya dadas por varios carteles. Tomemos esto uno por uno:
Correcto, no existe una jvm real, tal es solo una especificación, un montón de ciencias de la computación que describen un comportamiento deseado ... Hace poco busqué la inicialización de objetos Java a partir de código nativo. Para obtener lo que desea, la única forma es hacer lo que se llama anulación agresiva. Los errores si se hacen mal son tan malos que tenemos que limitarnos al alcance original de la pregunta:
La mayoría de los carteles aquí supondrán que está diciendo que está trabajando para una interfaz, de ser así, tendríamos que ver si le están entregando el objeto completo o un artículo a la vez.
Si ya no necesita un objeto, puede asignar un valor nulo al objeto, pero si se equivoca, se genera una excepción de puntero nulo. Apuesto a que puedes lograr un mejor trabajo si usas NIO
Cada vez que usted o yo o cualquier otra persona recibe: " Por favor, lo necesito horriblemente " . Es un precursor casi universal de la destrucción casi total de lo que está tratando de trabajar ... escríbanos un pequeño código de muestra, desinfectando cualquier código real utilizado y muéstrenos su pregunta.
No te frustres. A menudo, lo que esto resuelve es que su dba está usando un paquete comprado en algún lugar y el diseño original no está ajustado para estructuras de datos masivas.
Eso es muy comun.
FYI
La llamada al método System.runFinalizersOnExit (true) garantiza que se invoquen los métodos finalizadores antes de que Java se cierre. Sin embargo, este método es inherentemente inseguro y ha quedado en desuso. Una alternativa es agregar "ganchos de apagado" con el método Runtime.addShutdownHook.
Masarrat Siddiqui
Hay alguna forma indirecta de forzar al recolector de basura. Solo necesita llenar el montón con objetos temporales hasta el punto en que se ejecutará el recolector de basura. He hecho una clase que fuerza al recolector de basura de esta manera:
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;
}
}
Uso:
GarbageCollectorManager manager = new GarbageCollectorManager();
int iterationsUntilGcExecuted = manager.forceGarbageCollection();
No sé cuánto es útil este método, porque llena el montón constantemente, pero si tiene una aplicación de misión crítica que DEBE forzar GC, cuando esta puede ser la forma portátil de Java de forzar GC.
Me gustaría agregar algo aquí. Tenga en cuenta que Java no se ejecuta en una máquina virtual y no en una máquina real. La máquina virtual tiene su propia forma de comunicación con la máquina. Puede variar de un sistema a otro. Ahora, cuando llamamos al GC, le pedimos a la máquina virtual de Java que llame al recolector de basura.
Dado que el recolector de basura está con la máquina virtual, no podemos obligarlo a hacer una limpieza allí mismo. En lugar de hacer cola nuestra solicitud con el recolector de basura. Depende de la máquina virtual, después de un tiempo particular (esto puede cambiar de un sistema a otro, generalmente cuando la memoria de umbral asignada a la JVM está llena) la máquina real liberará el espacio. :RE
El siguiente código está tomado del método afirmarGC (...). Intenta forzar al recolector de basura no determinista a recolectar.
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);
Fuente (agregué algunos comentarios para mayor claridad): Ejemplo de NbTestCase
Puede intentar usar Runtime.getRuntime().gc()
o usar el método de utilidad System.gc()
Nota: estos métodos no garantizan GC. Y su alcance debe limitarse a JVM en lugar de manejarlo programáticamente en su aplicación.