Java primitivi naspram objekata

1. Pregled

U ovom uputstvu prikazujemo prednosti i nedostatke korištenja Java primitivnih tipova i njihovih omotanih pandana.

2. Sustav Java tipa

Java ima dvostruki sustav tipa koji se sastoji od primitiva poput int, boolean i referentne vrste kao što su Cijeli broj,Booleova. Svaki primitivni tip odgovara referentnom tipu.

Svaki objekt sadrži jednu vrijednost odgovarajućeg primitivnog tipa. The klase omota su nepromjenjive (tako da se njihovo stanje ne može promijeniti nakon što je objekt konstruiran) i konačni su (tako da ih ne možemo naslijediti).

Ispod napa, Java vrši pretvorbu između primitivnog i referentnog tipa ako se stvarni tip razlikuje od deklariranog:

Cijeli broj j = 1; // autoboxing int i = novi cijeli broj (1); // Raspakirati 

Postupak pretvaranja primitivnog tipa u referentni naziva se autoboksiranje, a suprotan postupak se naziva unboxing.

3. Prednosti i nedostaci

Odluka koji će se objekt koristiti temelji se na tome kakvu izvedbu aplikacije pokušavamo postići, koliko imamo dostupne memorije, količinu dostupne memorije i koje zadane vrijednosti trebamo obraditi.

Ako se ne suočimo s nijednim od tih, možda ćemo zanemariti ta razmatranja iako ih vrijedi znati.

3.1. Otisak memorije jednog predmeta

Samo za referencu, varijable primitivnog tipa imaju sljedeći utjecaj na memoriju:

  • boolean - 1 bit
  • bajt - 8 bitova
  • kratko, char - 16 bitova
  • int, float - 32 bita
  • dugo, dvostruko - 64 bita

U praksi se ove vrijednosti mogu razlikovati ovisno o implementaciji virtualnog stroja. U Oracleovom VM-u, logički se tip, na primjer, preslikava na int vrijednosti 0 i 1, tako da treba 32 bita, kako je ovdje opisano: Primitivni tipovi i vrijednosti.

Varijable ovih vrsta žive u hrpi i stoga im se brzo pristupa. Za detalje preporučujemo naš vodič o Java modelu memorije.

Referentne vrste su objekti, žive na hrpi i relativno im je spor pristup. Imaju određenih troškova u odnosu na svoje primitivne kolege.

Konkretne vrijednosti općih troškova općenito su specifične za JVM. Ovdje predstavljamo rezultate za 64-bitni virtualni stroj sa sljedećim parametrima:

java 10.0.1 2018-04-17 Java (TM) SE Runtime Environment 18.3 (build 10.0.1 + 10) Java HotSpot (TM) 64-bitni poslužitelj VM 18.3 (build 10.0.1 + 10, mješoviti način)

Da bismo dobili unutarnju strukturu objekta, možemo se poslužiti alatom Java Object Layout (pogledajte naš drugi vodič o tome kako dobiti veličinu objekta).

Ispada da jedan primjerak referentnog tipa na ovom JVM zauzima 128 bita, osim Dugo i Dvostruko koji zauzimaju 192 bita:

  • Boolean - 128 bita
  • Bajt - 128 bita
  • Kratko, karakter - 128 bita
  • Cijeli broj, plutajući - 128 bita
  • Dugo, dvostruko - 192 bita

Možemo vidjeti da jedna varijabla od Booleova tip zauzima toliko prostora kao 128 primitivnih, dok jedan Cijeli broj varijabla zauzima toliko prostora kao četiri int one.

3.2. Otisak memorije za nizove

Situacija postaje zanimljivija ako usporedimo koliko memorije zauzimaju nizovi razmatranih tipova.

Kada za svaku vrstu stvorimo nizove s raznim brojem elemenata, dobivamo grafički prikaz:

to pokazuje da su tipovi grupirani u četiri obitelji s obzirom na to kako pamćenje m (s) ovisi o broju elemenata s niza:

  • dugo, dvostruko: m (s) = 128 + 64 s
  • kratko, char: m (s) = 128 + 64 [s / 4]
  • bajt, logička vrijednost: m (s) = 128 + 64 [s / 8]
  • ostalo: m (s) = 128 + 64 [s / 2]

gdje uglate zagrade označavaju standardnu ​​stropnu funkciju.

Iznenađujuće, nizovi primitivnih tipova dugo i dvostruko troše više memorije od njihovih klasa omota Dugo i Dvostruko.

Možemo vidjeti i to jednoelementni nizovi primitivnih tipova gotovo su uvijek skuplji (osim dugih i dvostrukih) od odgovarajućeg referentnog tipa.

3.3. Izvođenje

Izvedba Java koda prilično je suptilno pitanje, uvelike ovisi o hardveru na kojem se kôd izvodi, o prevoditelju koji bi mogao izvršiti određene optimizacije, o stanju virtualnog stroja, o aktivnosti drugih procesa u operacijski sustav.

Kao što smo već spomenuli, primitivni tipovi žive u hrpi dok referentni tipovi žive u hrpi. Ovo je dominantan čimbenik koji određuje brzinom pristupa objektima.

Da bismo demonstrirali koliko su operacije za primitivne tipove brže od onih za klase omota, stvorimo niz od pet milijuna elemenata u kojem su svi elementi jednaki, osim posljednjeg; onda izvodimo pretragu za taj element:

while (! pivot.equals (elements [index])) {index ++; }

i usporedite izvedbu ove operacije za slučaj kada niz sadrži varijable primitivnih tipova i za slučaj kada sadrži objekte referentnih tipova.

Koristimo dobro poznati alat za usporedbu JMH (pogledajte naš vodič o tome kako ga koristiti), a rezultati operacije pretraživanja mogu se sažeti u ovoj tablici:

Čak i za tako jednostavnu operaciju možemo vidjeti da je potrebno više vremena za izvođenje operacije za klase omota.

U slučaju složenijih operacija poput zbrajanja, množenja ili dijeljenja, razlika u brzini može skočiti u nebo.

3.4. Zadane vrijednosti

Zadane vrijednosti primitivnih tipova su 0 (u odgovarajućem prikazu, t.j. 0, 0,0 d itd.) za numeričke tipove, lažno za logički tip, \ u0000 za tip char. Za klase omota zadana je vrijednost null.

To znači da primitivni tipovi mogu stjecati vrijednosti samo iz svojih domena, dok referentni tipovi mogu stjecati vrijednost (null) koji u nekom smislu ne pripada njihovim domenama.

Iako se ne smatra dobrom praksom ostavljati varijable neinicijaliziranim, ponekad bismo mogli dodijeliti vrijednost nakon njenog stvaranja.

U takvoj situaciji, kada primitivna varijabla tipa ima vrijednost koja je jednaka zadanoj vrsti, trebali bismo otkriti je li varijabla zaista inicijalizirana.

Ne postoji takav problem s varijablama klase omotača od null vrijednost sasvim je očit pokazatelj da varijabla nije inicijalizirana.

4. Upotreba

Kao što smo vidjeli, primitivni tipovi su puno brži i zahtijevaju mnogo manje memorije. Stoga bismo ih možda željeli radije koristiti.

S druge strane, trenutna specifikacija Java jezika ne dopušta upotrebu primitivnih tipova u parametriziranim tipovima (generički), u Java kolekcijama ili Reflection API-ju.

Kada našoj aplikaciji trebaju zbirke s velikim brojem elemenata, trebali bismo razmotriti upotrebu nizova sa što ekonomičnijim tipom, kao što je prikazano na gornjoj plohi.

5. Zaključak

U ovom vodiču ilustrirali smo da su objekti u Javi sporiji i imaju veći utjecaj na memoriju od njihovih primitivnih analoga.

Kao i uvijek, isječke koda možete pronaći u našem spremištu na GitHubu.