Dlaczego wartości buforowania klasy Integer mieszczą się w zakresie od -128 do 127?

81

Jeśli chodzi o moje poprzednie pytanie, dlaczego == porównania z Integer.valueOf (String) dają różne wyniki dla 127 i 128? , wiemy, że Integer classma pamięć podręczną, która przechowuje wartości między -128a 127.

Zastanawiam się tylko, dlaczego między -128 a 127 ?

Dokumentacja Integer.valueOf () stwierdza, że " buforuje często żądane wartości " . Ale czy wartości pomiędzy -128i 127są często wymagane naprawdę? Myślałem, że często wymagane wartości są bardzo subiektywne.
Czy jest jakikolwiek możliwy powód?

W dokumentacji stwierdzono również: „... i może buforować inne wartości spoza tego zakresu.
Jak można to osiągnąć?

DnR
źródło
7
Do dokumentacji: Oracle tylko zakrywa swoje tyłki na wypadek, gdyby zdecydowali się później zmienić zachowanie. Na przykład mogą zdecydować, że Java 9 będzie buforować od -1024 do 1023. Wiadomość nie polega na pamięci podręcznej zawierającej lub niezawierającej określonej liczby całkowitej.
Dawood ibn Kareem,
7
Zakładam, że dużo częściej zapętlasz od 0 do X niż od 13476 do Y. Musieli zdecydować, że należy również uwzględnić wartości ujemne, a -128 -> 127 ma sens dla bajtu ze znakiem.
Jeroen Vannevel
2
Czy pętla nie jest prawie zawsze wykonywana z pierwotnymi liczbami całkowitymi - a nie opakowanymi liczbami całkowitymi? Buforowanie nie ma zastosowania.
bradvido
2
Pamięć podręczna to kwestia wyłącznie wydajności. Dopóki nie powoduje to problemów z wydajnością, nie powinno obchodzić, jaki zakres jest buforowany. (Byłoby szczytem szaleństwa wbudowanie w swój kod zależności od buforowania typu Integer.)
Hot Licks,
3
@JohnR jest w specyfikacji języka Java, zobacz odpowiedź assylias poniżej.
Zac Thompson,

Odpowiedzi:

105

Zastanawiam się tylko, dlaczego między -128 a 127?

W pamięci podręcznej może być przechowywany większy zakres liczb całkowitych , ale przynajmniej te między -128 a 127 muszą być buforowane, ponieważ jest to wymagane przez specyfikację języka Java (wyróżnienie moje):

Jeśli wartość p w ramce to prawda, fałsz, bajt lub znak w zakresie od \ u0000 do \ u007f lub liczba int lub krótka z zakresu od -128 do 127 (włącznie) , niech r1 i r2 będą wynikiem dowolne dwie konwersje bokserskie p. Zawsze jest tak, że r1 == r2.

Uzasadnienie tego wymogu wyjaśniono w tym samym akapicie:

Idealnie byłoby, gdyby opakowanie danej pierwotnej wartości p zawsze dawało identyczne odniesienie . W praktyce może to być niewykonalne przy użyciu istniejących technik wdrożeniowych. Powyższe zasady to pragmatyczny kompromis. Ostatnia klauzula powyżej wymaga, aby pewne wspólne wartości były zawsze opakowane w nierozróżnialne obiekty. […]

Zapewnia to, że w większości przypadków zachowanie będzie pożądane, bez narzucania nadmiernej utraty wydajności, szczególnie na małych urządzeniach . Implementacje z mniejszą ilością pamięci mogą na przykład buforować wszystkie wartości char i short, a także wartości int i long w zakresie od -32K do + 32K.


Jak mogę buforować inne wartości spoza tego zakresu.?

Możesz użyć -XX:AutoBoxCacheMaxopcji JVM, która nie jest tak naprawdę udokumentowana na liście dostępnych opcji JVM Hotspot . Jest to jednak wspomniane w komentarzach wewnątrz Integerklasy wokół wiersza 590 :

Rozmiar pamięci podręcznej można kontrolować za pomocą -XX:AutoBoxCacheMax=<size>opcji.

Należy pamiętać, że jest to specyficzne dla implementacji i może, ale nie musi być dostępne w innych maszynach JVM.

asylias
źródło
2
To jest pełna i najlepsza odpowiedź - pytanie myli zakres od -128 do 127 z „często żądanymi wartościami”, podczas gdy w rzeczywistości są one z różnych powodów. -128 do 127 są buforowane na potrzeby boksu. „często żądane wartości” są buforowane w celu zapewnienia wydajności.
Zac Thompson,
@ZacThompson, dziękuję za wskazanie tego. Mój poprzedni komentarz nie był poprawny. Frazą kluczową ze specyfikacji jest „an int ... między -128 a 127 (włącznie), a następnie niech r1 i r2 będą wynikami dowolnych dwóch konwersji typu boxing p. Zawsze jest tak, że r1 == r2”. Więc jeśli dobrze rozumiem, specyfikacja wymaga, aby Integer.valueOf (X) == Integer.valueOf (X), gdzie -128 <= X <= 127.
John R
Jest to jedyna odpowiedź na część pytania „dlaczego”, która oferuje coś innego niż „to ustawienie domyślne”. Jednak ta odpowiedź nie jest kompletna, ponieważ nie dotyczy części pytania „jak”. Odwołanie się do odpowiedzi innych osób na XX: AutoBoxCacheMax i dodanie informacji o tym, jak kontrolować zachowanie buforowania w innych implementacjach JVM (lub wskazanie, które implementacje JVM mają opcje kontrolowania tego zachowania) uczyniłoby to pełną odpowiedź.
John R
„W praktyce może to być niewykonalne przy użyciu istniejących technik wdrożeniowych”. Nie mogę dostać tej linii. Czy możesz to wyjaśnić?
niiraj874u
2
@ niiraj874u Bieżąca implementacja używa pamięci podręcznej, która znajduje się w pamięci - każda „kanoniczna” liczba całkowita jest przechowywana w tej pamięci podręcznej. Zatem buforowanie wszystkich liczb całkowitych oznaczałoby, że będziesz musiał przechowywać w pamięci do 2 ^ 32 liczb całkowitych (= 15+ GB), co jest nieracjonalne, nawet na nowoczesnym komputerze stacjonarnym.
assylias
22

-128 do 127 to rozmiar domyślny. Ale javadoc mówi również, że rozmiar pamięci podręcznej Integer może być kontrolowany przez -XX:AutoBoxCacheMax=<size>opcję. Zauważ, że ustawia tylko wysoką wartość, niska wartość to zawsze -128. Ta funkcja została wprowadzona w wersji 1.6.

Co do tego, dlaczego od -128 do 127 - jest to zakres wartości bajtów i naturalne jest użycie go dla bardzo małej pamięci podręcznej.

Evgeniy Dorofeev
źródło
jak możemy wdrożyć -XX:AutoBoxCacheMax=<size>?
DnR,
uruchom java -XX: AutoBoxCacheMax = 256 ... a zobaczysz, że Integer.valueOf (256) == Integer.valueOf (256)
Evgeniy Dorofeev
uruchamiając java -XX:AutoBoxCacheMax=256w konsoli, otrzymałemError:could not create the Java Virtual Machine
DnR
spróbuj java - wersja powinna być 1.6 lub nowsza, mój 1.7 działa OK
Evgeniy Dorofeev
2
Właśnie dlatego javadoc mówi ... może być kontrolowane ... moja Java ma 64 bity
Evgeniy Dorofeev,
5

Powodem buforowania małych liczb całkowitych, jeśli o to pytasz, jest to, że wiele algorytmów używa małych liczb całkowitych w swoich obliczeniach, więc unikanie narzutu tworzenia obiektów dla tych wartości wydaje się być opłacalne.

Powstaje wtedy pytanie, które liczby całkowite mają być buforowane. Ponownie, mówiąc ogólnie, częstotliwość, z jaką używane są wartości stałe, ma tendencję do zmniejszania się wraz ze wzrostem wartości bezwzględnej stałej - wszyscy spędzają dużo czasu na wartościach 1, 2 lub 10, stosunkowo niewielu bardzo używa wartości 109 intensywnie; mniej będzie miało wydajność zależną od tego, jak szybko można uzyskać liczbę całkowitą dla 722. Java zdecydowała się przydzielić 256 przedziałów obejmujących zakres wartości bajtu ze znakiem. Decyzja ta mogła zostać oparta na analizie programów istniejących w tamtym czasie, ale równie prawdopodobne jest, że była to decyzja czysto arbitralna. Jest to rozsądna ilość miejsca do zainwestowania, można uzyskać do niej szybki dostęp (maska, aby dowiedzieć się, czy wartość mieści się w zakresie pamięci podręcznej, a następnie szybkie wyszukiwanie w tabeli, aby uzyskać dostęp do pamięci podręcznej) i na pewno obejmie najczęstsze przypadki.

Innymi słowy, myślę, że odpowiedź na twoje pytanie brzmi: „nie jest tak subiektywne, jak myślałeś, ale dokładne granice są w dużej mierze decyzją opartą na regułach ... a eksperymentalne dowody wskazują, że było wystarczająco dobre. "

keszlam
źródło
3

Maksymalną wartość całkowitą, która może być buforowana, można skonfigurować za pomocą właściwości systemowej, tj. java.lang.Integer.IntegerCache.high( -XX:AutoBoxCacheMax). Pamięć podręczna jest implementowana przy użyciu tablicy.

    private static class IntegerCache {
    static final int high;
    static final Integer cache[];

    static {
        final int low = -128;

        // high value may be configured by property
        int h = 127;
        if (integerCacheHighPropValue != null) {
            // Use Long.decode here to avoid invoking methods that
            // require Integer's autoboxing cache to be initialized
            int i = Long.decode(integerCacheHighPropValue).intValue();
            i = Math.max(i, 127);
            // Maximum array size is Integer.MAX_VALUE
            h = Math.min(i, Integer.MAX_VALUE - -low);
        }
        high = h;

        cache = new Integer[(high - low) + 1];
        int j = low;
        for(int k = 0; k < cache.length; k++)
            cache[k] = new Integer(j++);
    }

    private IntegerCache() {}
}
czas egzaminu
źródło
0

Kiedy napotkasz klasę Integer i zawsze masz ramkę w zakresie od -128 do 127, zawsze lepiej jest przekonwertować obiekt Integer na wartość int, jak poniżej.

<Your Integer Object>.intValue()
Teja
źródło