Java osnovni tipovi podataka keš memorija (IntegerCache)
„Sanmej, danas ćemo nadoknaditi jedno malo znanje: Java keš memorija za osnovne tipove podataka.“ Popio sam sam fanfom čaja i rekao Sanmej, „postaviću ti jedno pitanje: koji je razliku između new Integer(18) i Integer.valueOf(18)?“
„Zar nisu isti?“ Sanmej je bila iznenađena.
„Nisu isti.“ Smeh sam se.
new Integer(18)svaki put kreira novi objekat;Integer.valueOf(18)koristi objekat iz keš memorije, više poziva će uzeti isti referencu na objekat.
Pogledajmo sledeći kod:
Integer x = new Integer(18);
Integer y = new Integer(18);
System.out.println(x == y);
Integer z = Integer.valueOf(18);
Integer k = Integer.valueOf(18);
System.out.println(z == k);
Integer m = Integer.valueOf(300);
Integer p = Integer.valueOf(300);
System.out.println(m == p);Pogledajmo rezultat:
false
true
false„Prvi false, znam zašto, jer new kreira različite objekte s različitim adresama.“ Sanmej je objasnila, „mislim da bi drugi i treći trebali biti true, zašto treći daje false? To ne razumem.“
„Zapravo je razlog vrlo jednostavan.“ Siguran sam.
Omotni klasi za osnovne tipove podataka, osim Float i Double, ostalih šest omotačkih klasa (Byte, Short, Integer, Long, Character, Boolean) imaju konstantnu keš memoriju.
- Byte: -128~127, odnosno sve bajt vrednosti
- Short: -128~127
- Long: -128~127
- Character: \u0000 - \u007f
- Boolean: true i false
Uzimajući Integer kao primer, unutar Integer klase ugrađeno je 256 keširanih Integer podataka, kada se koristi podatak u opsegu -128~127, direktno vraća referencu na podatak iz konstantne memorije, umesto kreiranja novog objekta, van ovog opsega kreira novi objekat.
18 je u -128~127, 300 nije.
Pogledajmo izvorni kod valueOf metode.
public static Integer valueOf(int i) {
if (i >=IntegerCache.low && i <=IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}„Oh, zbog Integer.IntegerCache ove unutrašnje klase!“ Sanmej je kao da je otkrila novi kontinent.
„Da. Pogledajmo izvorni kod ove statičke unutrašnje klase IntegerCache.“
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high vrednost može biti konfigurisana osobinom
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maksimalna veličina niza je Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// Ako se osobina ne može parsirati u int, ignorisati je.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// opseg [-128, 127] mora biti interniran (JLS7 5.1.7)
assert Integer.IntegerCache.high >= 127;
}
private IntegerCache() {}
}Detaljno objašnjenje: kada dobijamo objekat celog broja kroz Integer.valueOf() metod, prvo proveravamo da li je taj ceo broj u IntegerCache, ako jeste, vraća objekat iz keš memorije, inače kreira novi objekat i kešira ga.
Treba primetiti, ako koristite new Integer() za kreiranje objekta, čak i ako je vrednost u opsegu -128 do 127, neće biti keširana, svaki put kreira novi objekat. Stoga se preporučuje korišćenje Integer.valueOf() metoda za dobijanje celobrojnog objekta.
Pri učenju static ključne reči, detaljno objašnjavamo statički blok koda, privremeno zapamti, Sanmej, statički blok koda se obično koristi za inicijalizaciju nekih statičkih promenljivih, izvršava se pre main() metode.
U statičkom bloku koda, low je -128, odnosno minimalna vrednost keš memorije; high je podrazumevano 127, odnosno maksimalna vrednost keš memorije, ukupno 256.
Može se podesiti veličina keš memorije pri pokretanju JVM pomoću -XX:AutoBoxCacheMax=NNN, naravno, ne može biti beskonačna, maksimalno do Integer.MAX_VALUE -129
Zatom, inicijalizuje se veličina cache niza, zatim se popunjava prolaskom, indeks počinje od 0.
„Razumeš? Sanmej.“ Popio sam vode i pogledao Sanmej pored sebe.
„Ovaj kod nije težak za razumeti, teško je razumeti assert Integer.IntegerCache.high >= 127;, šta radi ova linija koda?“ Sanmej je bila zbunjena.
„Oh, vrlo si pažljiva!“ Sanmej je stvarno dobra, želja za znanjem je sve jača.
assert je ključna reč u Javi, znači tvrdnju, za olakšavanje debugovanja programa, nije deo objavljivanja programa.
Podrazumevano, tvrdnja je isključena, može se uključiti dodavanjem -ea parametra prilikom pokretanja Java programa iz komandne linije.
Pogledajmo ovaj kod.
public class AssertTest {
public static void main(String[] args) {
int high = 126;
assert high >= 127;
}
}Pretpostavimo da je ručno podesena veličina keš memorije 126, očito ne odgovara očekivanoj vrednosti keš memorije 127, šta će se ispisati?
Direktno otvorite komandni terminal u Intellij IDEA, uđite u fajlove, izvršite:
/usr/libexec/java_home -v 1.8 --exec java -ea com.itwanger.s51.AssertTestKoristim macOS okruženje, instalirao sam nekoliko verzija JDK, ova komanda može prebaciti na JDK 8
Može se izvršiti bez specificiranja Java verzije (dodajte -ea parametar):
java -ea com.itwanger.s51.AssertTest„Oh, greška!“ Sanmej je vikala.
Exception in thread "main" java.lang.AssertionError
at com.itwanger.s51.AssertTest.main(AssertTest.java:9)„Da, jer je 126 manje od 127.“ Odgovorio sam.
„Originalno assert se koristi ovako, razumem.“ Sanmej pokazuje da je naučila.
U Javi, za neke osnovne tipove podataka (kao Integer, Long, Boolean itd.), Java će pri pokretanju programa kreirati neke često korišćene objekte i keširati ih u memoriji, da bi poboljšala performanse programa i uštedela memoriju. Ovi često korišćeni objekti su keširani u fiksnom opsegu, vrednosti van ovog opsega će ponovo kreirati nove objekte.
Korišćenje keš memorije za tipove podataka može efikasno poboljšati performanse programa i uštedeti memoriju, ali treba primetiti da u specifičnim poslovnim scenarijima, keš memorija može doneti neke probleme, na primer keširani objekti su modifikovani istovremeno od različitih niti, što dovodi do pogrešnih podataka itd. Stoga, u stvarnom razvoju, treba odlučiti da li koristiti keš memoriju za tipove podataka prema konkretnim poslovnim potrebama.
