2011-07-19 13 views
14

estaba pensando en la detección de fugas de memoria automática para un programa Java. El algoritmo básico es crear JUnits que contienen la siguiente lógica:automatizado de detección de fugas de memoria en Java

Call System.gc() several times 
Determine initial heap memory consumption using either Runtime class or JMX 
Loop 
    Do something that exercises program under test 
End loop 

Call System.gc() several times 
Determine final heap memory consumption 
Compare initial and final memory numbers 

El bucle está siendo utilizado para ver si la memoria está subiendo en pequeños incrementos.

Es necesario distinguir entre los aumentos esperados e inesperados en el uso de la memoria.

Esto no es realmente una prueba de unidad. Pero el marco JUnit es conveniente de usar.

¿Cree que este enfoque es válido? ¿Cree que este enfoque será exitoso para identificar fugas de memoria? ¿Alguna vez has hecho algo como esto?

+0

una vez estaba pensando en una prueba de este tipo también, pero no pude encontrar una buena solution..hopefully uno va a venir aquí. – mort

Respuesta

6

No se puede hacer esto con Java. El recolector de basura se ejecutará cuando determine que es necesario. Además de esto, puede "liberar" la memoria para que pueda ser reutilizada, pero eso no significa que desasignará el bloque.

0

Al menos debe ejecutar primero la prueba antes de realizar la prueba para eliminar objetos de una sola vez que deben crearse solo una vez. Lo mismo ocurre con:

test() 
checkMem() 
test() 
checkMem() 
compareIfMemUsageHasNotIncreased() 
4

Ese no es un enfoque significativo en Java. System.gc() no le da ninguna garantía razonable e incluso si se convence a sí mismo de que hay un problema, este enfoque no lo ayudará a encontrar ese problema.

En su lugar, debe utilizar un generador de perfiles de memoria. Si no desea pagar por uno profesional, puede probar el jvisualvm que está instalado junto con la JVM.

+0

JConsole viene con el JDK y creo que es más que adecuado para la mayoría de los casos de uso. – Dunes

+0

¡JConsole es realmente agradable! Netbeans también viene con útiles herramientas de creación de perfiles. – mort

+0

Creo que el generador de perfiles de Netbeans y JConsole/jvisualvm son la misma herramienta, esta última es independiente de Netbeans. –

17

Desarrollé un marco de prueba de unidad simple para fugas de memoria que me ha funcionado de manera confiable. La idea básica es crear una referencia débil a un objeto que debe recogerse como basura, ejecutar la prueba, realizar un GC completo y luego verificar que la referencia débil se haya eliminado.

que aquí hay una prueba de regresión bastante típico usando mi marco:

public void testDS00032554() throws Exception { 
    Project testProject = getTestProject(); 
    MemoryLeakVerifier verifier = new MemoryLeakVerifier(new RuntimeTestAction(getTestClassMap())); 
    testProject.close(); 
    verifier.assertGarbageCollected("RuntimeTestAction should be garbage collected when project closed"); 
} 

hay algunas cosas a tener en cuenta aquí:

  1. Es crítico que el objeto que desea tener ser recogidos no debería almacenarse en una variable en la prueba de su unidad, ya que se conservará hasta el final de su prueba.
  2. Esta es una técnica útil para las pruebas de regresión donde se informó una fuga y usted sabe qué objeto debería haberse eliminado.
  3. Un problema con este enfoque es que es difícil determinar por qué falló la prueba. En este punto, necesitará un generador de perfiles de memoria (soy parcial de YourKit). Sin embargo, aún es útil tener las pruebas de regresión para que las fugas no puedan reintroducirse accidentalmente en el futuro.
  4. me encontré con algunos problemas de roscado con no todas las referencias que se borran de inmediato, por lo que el método que ahora intenta realizar la GC un número de veces antes de fallar (como se describe en este artículo: Java Tip 130: Do you know your data size?)

Aquí está la completa clase de ayuda en caso de que quiera probarlo:

/** 
* A simple utility class that can verify that an object has been successfully garbage collected. 
*/ 
public class MemoryLeakVerifier { 
private static final int MAX_GC_ITERATIONS = 50; 
private static final int GC_SLEEP_TIME  = 100; 

private final WeakReference reference; 

public MemoryLeakVerifier(Object object) { 
    this.reference = new WeakReference(object); 
} 

public Object getObject() { 
    return reference.get(); 
} 

/** 
* Attempts to perform a full garbage collection so that all weak references will be removed. Usually only 
* a single GC is required, but there have been situations where some unused memory is not cleared up on the 
* first pass. This method performs a full garbage collection and then validates that the weak reference 
* now has been cleared. If it hasn't then the thread will sleep for 50 milliseconds and then retry up to 
* 10 more times. If after this the object still has not been collected then the assertion will fail. 
* 
* Based upon the method described in: http://www.javaworld.com/javaworld/javatips/jw-javatip130.html 
*/ 
public void assertGarbageCollected(String name) { 
    Runtime runtime = Runtime.getRuntime(); 
    for (int i = 0; i < MAX_GC_ITERATIONS; i++) { 
     runtime.runFinalization(); 
     runtime.gc(); 
     if (getObject() == null) 
      break; 

     // Pause for a while and then go back around the loop to try again... 
     try { 
      EventQueue.invokeAndWait(Procedure.NoOp); // Wait for the AWT event queue to have completed processing 
      Thread.sleep(GC_SLEEP_TIME); 
     } catch (InterruptedException e) { 
      // Ignore any interrupts and just try again... 
     } catch (InvocationTargetException e) { 
      // Ignore any interrupts and just try again... 
     } 
    } 
    PanteroTestCase.assertNull(name + ": object should not exist after " + MAX_GC_ITERATIONS + " collections", getObject()); 
} 

}

+0

Esto es lo mejor que pude encontrar en la red. Lo acepto de inmediato :) – wuppi

+0

@Andy, ¿por qué es necesario Thread.sleep()? ¿No es suficiente seguir tratando de recoger basura? – Gili

+1

La intención con Thread.sleep es darle al recolector de basura un poco de tiempo para ponerse al día. Si el código no durmió, estaría en un bucle cerrado que pegaría la CPU hasta que el recolector de basura termine. –

Cuestiones relacionadas