2012-12-05 10 views
6

kod Java jest następujący:Czy odbiór śmieci ma miejsce natychmiast po wywołaniu Hashmap.remove()?

Random r = new Random(1234697890); 
HashMap<Integer, List<Integer>> map = new HashMap<Integer, List<Integer>>(); 
List<Integer> list = new ArrayList<Integer>(); 

for(int i=0;i<100000;i++){ 
    for(int j=0;j<1000;j++){ 
     list.add(r.nextInt(100000)); 
    } 
    map.put(i, list); 
    map.remove(i); 
} 

gdy i osiągnie 37553, java.lang.OutOfMemoryError: Java heap space dzieje.
Wygląda na to, że odśmiecanie nie ma miejsca w pętli.
Teraz zastanawiam się, jak rozwiązać problem.

+0

Zawsze dodajesz do listy i nigdy nie usuwasz z niej. –

+2

FYI, guava ma zestaw "Multimap", który implementuje 'Mapę >' i dba o tworzenie list dla ciebie. –

+0

@JohnB - jak to może mieć znaczenie dla pytania? – Perception

Odpowiedz

5

Spróbuj przepisanie kodu w następujący sposób i nie należy się OOME w ...

Random r = new Random(1234697890); 
HashMap<Integer, List<Integer>> map = new HashMap<Integer, List<Integer>>(); 

for(int i=0;i<100000;i++){ 
    List<Integer> list = new ArrayList<Integer>(); 
    for(int j=0;j<1000;j++){ 
     list.add(r.nextInt(100000)); 
    } 
    map.put(i, list); 
    map.remove(i); 
} 

Problem z oryginalnego kodu jest to, że:

  • utworzyć tylko jedną listę,
  • dodajesz do niego coraz więcej elementów, a lista kończy się tylko śmieciami, gdy kod zostanie ukończony ... ponieważ cały czas jest "w zasięgu".

Moving deklarację wewnątrz pętli list oznacza, że ​​nowy ArrayList jest tworzony i wypełnione w każdej iteracji pętli, a staje się śmieci po uruchomieniu kolejnej iteracji.


Ktoś zasugerował dzwoniąc pod numer System.gc(). Nie pomoże to w twoim przypadku, ponieważ do zebrania śmieci jest niewiele. A w ogóle to jest to zły pomysł, ponieważ:

  • GC jest zagwarantowane, że prowadzony bezpośrednio przed OOME jest wyrzucane,
  • JVM może dowiedzieć się lepiej niż można, gdy jest to najlepszy (czyli najbardziej wydajny) czas na uruchomienie GC,
  • Twój telefon do System.gc() może i tak zostać całkowicie zignorowany. JVM można skonfigurować tak, aby połączenia z numerem System.gc() były ignorowane.

1 - The pedant we mnie pragnie podkreślić, że map.put(i, list); map.remove(i); najprawdopodobniej generowania Integer obiektu, który najprawdopodobniej stanie się śmieci. Jednak jest to "karma dla kurcząt" w porównaniu do twojego nieskończenie rosnącego obiektu .

5

Używa się tej samej listy przez cały czas, która zawiera 100000 * 1000 elementów po zakończeniu pętli. Aby umożliwić GC pozbyć się listy, musisz zmniejszyć jej zasięg do pętli for(i).

Innymi słowy, zarówno mapa, jak i lista są dostępne w tym samym kodzie, a zatem nie kwalifikują się do odbioru.

+0

Alternatywnie, możesz zwiększyć rozmiar sterty, aby metoda mogła się zakończyć i wszystko może być GC'd wtedy: D – Esailija

0

W twoim przypadku nadal wypełniasz ten sam list (nawet jeśli usuniesz go z tej HashMap, nadal istnieje jako zmienna lokalna).

JVM obiecuje zrobić pełny garbage collect przed wyrzuceniem OutOfMemoryError. Więc możesz być pewny, że nie pozostało nic do posprzątania.

0

Kod

List<Integer> list = new ArrayList<Integer>(); 

for(int i=0;i<100000;i++){ 
    for(int j=0;j<1000;j++){ 
     list.add(r.nextInt(100000)); 
    } 
    map.put(i, list); 
    map.remove(i); 
} 

jest taka sama jak

List<Integer> list = new ArrayList<Integer>(); 

for(int i=0;i<100000 * 1000; i++) { 
    list.add(r.nextInt(100000)); 
} 

Jak widać jego wykazie nie mapę który zachowaniu wszystkich liczb całkowitych. BTW Spróbuj zamiast tego i zobacz, co się stanie;)

list.add(r.nextInt(128));