2011-07-19 15 views
14

Myślałem o automatycznym wykrywaniu wycieków pamięci dla programu Java. Podstawowy algorytm jest stworzenie JUnits które zawierały następującą logiką:Automatyczne wykrywanie wycieków pamięci w Javie

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 

Pętla jest stosowany do sprawdzenia, czy pamięć pełzanie w małych odstępach.

Konieczne jest rozróżnienie między oczekiwanym i nieoczekiwanym wzrostem zużycia pamięci.

To naprawdę nie jest test jednostkowy. Ale framework JUnit jest wygodny w użyciu.

Czy uważasz, że to podejście jest ważne? Czy myślisz, że to podejście będzie skuteczne w identyfikacji wycieków pamięci? Czy kiedykolwiek zrobiłeś coś takiego?

+0

kiedyś myślał o takim teście też, ale nie mógł znaleźć dobry solution..hopefully jeden pojawi się tutaj. – mort

Odpowiedz

6

Nie można tego zrobić w Javie. Śmieciarz będzie działał, gdy ustali, że jest to konieczne. Poza tym może "uwolnić" pamięć, aby można ją było ponownie wykorzystać, ale to nie znaczy, że zwolni blok.

0

Przynajmniej musisz wykonać test przed wykonaniem testu, aby wyeliminować jednorazowe obiekty, które trzeba utworzyć tylko raz. Tak więc:

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

To nie jest znaczące podejście w Javie. System.gc() nie daje żadnych rozsądnych gwarancji, a nawet jeśli przekonasz się, że jest problem, takie podejście nie pomoże ci znaleźć tego problemu.

Zamiast tego należy użyć profilera pamięci. Jeśli nie chcesz płacić za profesjonalną, możesz wypróbować wersję jvisualvm zainstalowaną wraz z maszyną JVM.

+0

JConsole pochodzi z JDK i myślę, że to więcej niż wystarczające dla większości przypadków użycia. – Dunes

+0

JConsole jest naprawdę ładne! Netbeans jest również wyposażony w przydatne narzędzia do profilowania. – mort

+0

Myślę, że profiler Netbeans i JConsole/jvisualvm są tym samym narzędziem, drugie jest niezależne od Netbeans. –

17

Opracowałem prosty szkielet testowy dla wycieków pamięci, który zadziałał niezawodnie. Podstawową ideą jest utworzenie słabego odniesienia do obiektu, który powinien być zbiorem śmieci, wykonanie testu, wykonanie pełnego GC, a następnie sprawdzenie, czy słabe odniesienie zostało usunięte.

Tutaj jest dość typowe testy regresji używając mojego ramowa:

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"); 
} 

Istnieje kilka rzeczy do odnotowania tutaj:

  1. Bardzo ważne jest, że przedmiotem chcesz mieć być gromadzone nie powinien być przechowywane w zmiennej w teście jednostki, ponieważ zostanie zachowane do końca testu.
  2. Jest to przydatna technika do testów regresji, w których zgłoszono wyciek i wiadomo, który obiekt powinien zostać usunięty.
  3. Jednym z problemów związanych z tym podejściem jest to, że trudno jest stwierdzić, że zakończyło się niepowodzeniem testu. W tym momencie będziesz potrzebował profilera pamięci (jestem niepełny dla YourKit). Jednak IMO nadal jest przydatna w testach regresji, aby nie doszło do przypadkowego przywrócenia wycieków w przyszłości.
  4. wpadłem na pewne problemy gwintowania z nie wszystkie odniesienia są usuwane natychmiast, więc metoda teraz próbuje wykonywania GC kilka razy zanim braku (jak opisane w tym artykule: Java Tip 130: Do you know your data size?)

Oto pełna klasa pomocnika w przypadku, gdy chcesz go wypróbować:

/** 
* 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

To najlepsza rzecz, jaką mogłem znaleźć w sieci. Natychmiast go zaakceptuję :) – wuppi

+0

@Andy, dlaczego Thread.sleep() jest niezbędny? Czy nie wystarczy po prostu próbować zbierać śmieci? – Gili

+1

Zamiarem z Thread.sleep jest zapewnienie garbage collectorowi trochę czasu na nadrobienie zaległości. Jeśli kod nie był uśpiony, byłby w ciasnej pętli, która unieruchamia procesor, dopóki garbage collector nie zakończy działania. –

Powiązane problemy