2010-07-09 16 views
8

Chcę użyć programu MapMaker do utworzenia mapy przechowującej duże obiekty, , która powinna zostać usunięta z pamięci podręcznej, jeśli nie ma wystarczającej ilości pamięci. Ten mały program demo wydaje się działać prawidłowo:Używanie programu MapMaker do utworzenia pamięci podręcznej

public class TestValue { 
    private final int id; 
    private final int[] data = new int[100000]; 

    public TestValue(int id) { 
     this.id = id; 
    } 

    @Override 
    protected void finalize() throws Throwable { 
     super.finalize(); 
     System.out.println("finalized"); 
    } 
} 


public class Main { 

    private ConcurrentMap<Integer, TestValue> cache; 
    MemoryMXBean memoryBean; 

    public Main() { 
     cache = new MapMaker() 
       .weakKeys() 
       .softValues() 
       .makeMap(); 
     memoryBean = ManagementFactory.getMemoryMXBean(); 
    } 

    public void test() { 
     int i = 0; 
     while (true) { 
      System.out.println("Etntries: " + cache.size() + " heap: " 
       + memoryBean.getHeapMemoryUsage() + " non-heap: " 
       + memoryBean.getNonHeapMemoryUsage()); 
      for (int j = 0; j < 10; j++) { 
       i++; 
       TestValue t = new TestValue(i); 
       cache.put(i, t); 
      } 
      try { 
       Thread.sleep(100); 
      } catch (InterruptedException ex) { 
      } 
     } 
    } 

    /** 
    * @param args the command line arguments 
    */ 
    public static void main(String[] args) { 
     Main m = new Main(); 
     m.test(); 
    } 

} 

Jednakże gdy zrobić to samo w mojej prawdziwej aplikacji, wpisy są zasadzie usuwane z pamięci podręcznej tak szybko, jak są one dodawane. W mojej prawdziwej aplikacji używam również liczb całkowitych jako kluczy, a wartości buforowane to bloki archiwalne odczytywane z dysku zawierającego pewne dane. Jeśli chodzi o I , słabe referencje nie są już używane, ponieważ nie są już używane, ponieważ nie są już używane, więc są one słabe, ponieważ są to słabe referencje o numerach od . Jeśli utworzyć mapę takiego:

data = new MapMaker() 
      .softValues() 
      .makeMap(); 

Wpisy są nigdy śmieci zebrane i otrzymuję out-of-memory błąd w moim programie testowym. Metoda finalizacji na rekordach TestValue nigdy nie jest wywoływana. Jeśli mogę zmienić metodę testową do następujących:

public void test() { 
    int i = 0; 
    while (true) { 
     for (final Entry<Integer, TestValue> entry : 
      data.entrySet()) { 
      if (entry.getValue() == null) { 
       data.remove(entry.getKey()); 
      } 
     } 
     System.out.println("Etntries: " + data.size() + " heap: " 
      + memoryBean.getHeapMemoryUsage() + " non-heap: " 
      + memoryBean.getNonHeapMemoryUsage()); 
     for (int j = 0; j < 10; j++) { 
      i++; 
      TestValue t = new TestValue(i); 
      data.put(i, t); 
     } 
     try { 
      Thread.sleep(100); 
     } catch (InterruptedException ex) { 
     } 
    } 
} 

wpisy są usuwane z pamięci podręcznej i finalizatora na testValue obiektów nazywa, ale po jakimś czasie również uzyskać out-of-memory błąd.

Moje pytanie brzmi: jaki jest właściwy sposób użycia MapMaker do utworzenia mapy , która może być używana jako pamięć podręczna? Dlaczego mój program testowy nie usuwa wpisów tak szybko, jak to możliwe, jeśli używam weakKeys? Czy można dodać kolejkę referencyjną do mapy pamięci podręcznej?

+0

ktoś może edytować kod, aby ułatwić czytać? – nanda

+0

Jestem trochę zaskoczony tym. Użyłem 'softValues' dokładnie w ten sam sposób i działało dobrze, a' SoftReference 's jest czyszczony, gdy pamięć kończy się. – finnw

Odpowiedz

3

Słabe klucze wydają się błędem. Spróbuj użyć silnych kluczy, ponieważ są one liczbami całkowitymi.

+0

Próbowałem tego i działa, jeśli wywołuję System.gc(), zanim utworzę nowy obiekt i dodać go do pamięci podręcznej. Jeśli tego nie zrobię, wcześniej lub później otrzymam wyjątek dotyczący braku pamięci. Czy to jest właściwe podejście, czy polecasz coś innego? – Michael

+0

Masz dwie wersje TestValue, jedna zawiera dużą tablicę, a jedna zawiera int. Czy testujesz z dużą tablicą? Jeśli nie, możliwe jest, że GC po prostu nie może zwolnić wystarczającej ilości pamięci. –

8

Jest wiele rzeczy, które mogą się dziać, ale w odniesieniu do twojego programu testowego używającego miękkich wartości: możesz uzyskać OutOfMemoryError nawet jeśli masz SoftReferences, które nie zostały jeszcze zebrane. To powtarza: możesz uzyskać OutOfMemoryError, nawet jeśli masz SoftReferences, które nie zostały jeszcze wyczyszczone.

SoftReferences są trochę dziwne, patrz http://jeremymanson.blogspot.com/2009/07/how-hotspot-decides-to-clear_07.html po opis aktualnej mechaniki. Prawdopodobnie w twoim przypadku testowym GC po prostu nie miał czasu na wykonanie dwóch pełnych GC.

Kiedy używałeś słabych kluczy, CG oczyścił je od razu i nie musiał czekać na pełną przerwę GC. (B/C WeakReferences zbierane są agresywnie.)

Moim zdaniem, jeśli chcesz pamięci podręcznej wrażliwe z kluczami Integer, to myślę, że właściwe jest następujący:

data = new MapMaker().softValues().makeMap(); 

Można łatwo program testowy, który wyrzuca OutOfMemoryError, ale jeśli twoja prawdziwa aplikacja jest nieco zachowana i nie poddaje się zbyt dużej presji, możesz być w porządku. SoftReferences są dość trudne do uzyskania.

Jeśli potrzebujesz używać System.gc(), unikaj braku pamięci, zaleciłbym zamiast tego przełączyć się na mapę LRU o ustalonym maksymalnym rozmiarze (zobacz przykład javadoc z java.util.LinkedHashMap.) Nie jest to współbieżne, ale spodziewam się, że w efekcie da ci to lepszą wydajność, niż wymaganie od systemu, aby wykonał kolekcję śmieci na pełną pauzę kilka dodatkowych razy.

Och, i ostatnia uwaga na temat klawiszy całkowitych i słabych klawiszy(): MapMaker używa porównania tożsamości dla klawiszy przy użyciu słabych lub miękkich klawiszy, a to jest trudne do zrobienia poprawnie. Świadczyć co następuje:

Map<Integer,String> map = new MapMaker().weakKeys().makeMap(); 
Integer a = new Integer(1); 
Integer b = new Integer(1); 
Integer c = 1; //auto box 
Integer d = 1; //auto box 
map.put(a, "A"); 
map.put(b, "B"); 
map.put(c,"C"); 
map.put(d,"D"); 
map.size() // size is 3; 

Powodzenia.

+0

+1, ponieważ nie zdawałem sobie sprawy, że można uzyskać wyjątek OutOfMemoryError, zanim wszystkie SoftReferences zostaną uruchomione w gc. –

Powiązane problemy