Programmēšana

Java lielums

2003. gada 26. decembris

J: Vai Java ir operators C, piemēram, sizeof ()?

A: Virspusēja atbilde ir tāda, ka Java nesniedz neko tādu kā C lielums (). Tomēr ņemsim vērā kāpēc Java programmētājs laiku pa laikam to varētu vēlēties.

C programmētājs pats pārvalda lielāko daļu datu struktūras atmiņas sadalījumu un lielums () ir neaizstājams, lai zinātu, kādi atmiņas bloku izmēri jāpiešķir. Turklāt C atmiņas sadalītājiem patīk malloc () gandrīz neko nedariet, ciktāl tas attiecas uz objekta inicializēšanu: programmētājam jāiestata visi objektu lauki, kas norāda uz citiem objektiem. Bet, kad viss ir pateikts un kodēts, C / C ++ atmiņas piešķiršana ir diezgan efektīva.

Salīdzinājumam - Java objektu piešķiršana un konstruēšana ir sasaistīti kopā (nav iespējams izmantot piešķirto, bet neinicializēto objektu instanci). Ja Java klase definē laukus, kas ir atsauces uz citiem objektiem, parasti tos iestata arī būvniecības laikā. Tāpēc Java objekta piešķiršana bieži piešķir daudzus savstarpēji saistītus objektu gadījumus: objekta grafiku. Kopā ar automātisko atkritumu savākšanu tas ir pārāk ērti un var likt justies kā nekad nevajadzētu uztraukties par Java atmiņas sadalījuma detaļām.

Protams, tas darbojas tikai vienkāršām Java lietojumprogrammām. Salīdzinot ar C / C ++, līdzvērtīgas Java datastruktūras mēdz aizņemt vairāk fiziskās atmiņas. Uzņēmuma programmatūras izstrādē tuvināšanās maksimālajai pieejamai virtuālajai atmiņai mūsdienu 32 bitu JVM ir izplatīts mērogojamības ierobežojums. Tādējādi Java programmētājs varētu gūt labumu lielums () vai kaut kas līdzīgs, lai sekotu līdzi tam, vai viņa datstruktūras kļūst pārāk lielas vai satur atmiņas vājās vietas. Par laimi Java refleksija ļauj diezgan viegli uzrakstīt šādu rīku.

Pirms turpināt, es atteikšos no dažām, bet nepareizām atbildēm uz šī raksta jautājumu.

Nepareizs: Sizeof () nav nepieciešams, jo Java pamata tipu izmēri ir fiksēti

Jā, Java int ir 32 biti visās JVM un visās platformās, taču šī ir tikai valodas specifikācijas prasība programmētājs uztverams šī veida datu platums. Šāda int būtībā ir abstrakts datu tips, un to var dublēt, teiksim, 64 bitu fiziskās atmiņas vārds 64 bitu mašīnā. Tas pats attiecas uz neprimitīviem tipiem: Java valodas specifikācijā nekas nav teikts par to, kā klases lauki būtu jāsaskaņo fiziskajā atmiņā vai ka Būla logu masīvu nevarēja ieviest kā kompaktu bitvektoru JVM.

Fallacy: Jūs varat izmērīt objekta lielumu, sērijveidojot to baitu straumē un aplūkojot iegūto straumes garumu

Iemesls tam nedarbojas tāpēc, ka sērijas izkārtojums tikai attāli atspoguļo patieso atmiņā esošo izkārtojumu. Viens vienkāršs veids, kā to redzēt, ir apskatīt, kā Stīgas sērijveida: atmiņā katru char ir vismaz 2 baiti, bet sērijveidā Stīgas ir UTF-8 kodēti, tāpēc jebkurš ASCII saturs aizņem uz pusi mazāk vietas.

Vēl viena darba pieeja

Jūs varētu atcerēties "Java Padoms 130: Vai zināt savu datu apjomu?" aprakstīts paņēmiens, kura pamatā ir liela skaita identisku klases gadījumu izveide un rūpīgi izmērīts iegūtais JVM izmantotā kaudzes lieluma pieaugums. Ja piemērojama, šī ideja darbojas ļoti labi, un es to faktiski izmantošu, lai sāktu šajā rakstā esošo alternatīvo pieeju.

Ņemiet vērā, ka Java Tip 130 Sizeof klasei ir nepieciešams mierīgs JVM (lai kaudzes aktivitāte notiktu tikai objektu piešķiršanas un atkritumu savākšanas dēļ, ko pieprasa mērīšanas pavediens), un tam ir nepieciešams liels skaits identisku objektu gadījumu. Tas nedarbojas, ja vēlaties izmērīt vienu lielu objektu (iespējams, kā daļu no atkļūdošanas izsekošanas izejas) un it īpaši, ja vēlaties pārbaudīt, kas to faktiski padarīja tik lielu.

Kāds ir objekta izmērs?

Iepriekš diskusija izceļ filozofisko aspektu: ņemot vērā, ka jūs parasti nodarbojaties ar objektu grafikiem, kāda ir objekta lieluma definīcija? Vai tas ir tikai pārbaudāmā objekta instances vai visa objekta instancē sakņotā datu diagrammas lielums? Pēdējais ir tas, kas praksē parasti ir svarīgāks. Kā redzēsiet, lietas ne vienmēr ir tik skaidras, taču iesācējiem varat sekot šai pieejai:

  • Objekta instanci var (aptuveni) izmērot, summējot visus tā statisko datu laukus (ieskaitot laukus, kas definēti virsklasēs)
  • Atšķirībā no, teiksim, C ++, klases metodes un to virtualitāte neietekmē objekta lielumu
  • Klases virssaskarnes neietekmē objekta lielumu (skatīt piezīmi šī saraksta beigās)
  • Pilnu objekta izmēru var iegūt kā aizvērumu visam objekta grafikam, kas sakņojas sākuma objektā
Piezīme: Jebkuras Java saskarnes ieviešana tikai atzīmē attiecīgo klasi un tās definīcijai nepievieno nekādus datus. Faktiski JVM pat neapstiprina, ka saskarnes ieviešana nodrošina visas saskarnē nepieciešamās metodes: pašreizējās specifikācijās tā ir stingri sastādītāja atbildība.

Lai sāktu procesu, primitīvu datu tipiem es izmantoju fiziskos izmērus, ko mēra pēc Java Tip 130's Sizeof klasē. Kā izrādās, parastajiem 32 bitu JVM ir līdzenums java.lang.Object aizņem 8 baitus, un pamata datu tipiem parasti ir vismazākais fiziskais lielums, kas var apmierināt valodas prasības (izņemot būla aizņem veselu baitu):

 // java.lang. Objekta čaumalas lielums baitos: public static final int OBJECT_SHELL_SIZE = 8; public static final int OBJREF_SIZE = 4; publiskais statiskais fināls LONG_FIELD_SIZE = 8; public static final int INT_FIELD_SIZE = 4; public static final int SHORT_FIELD_SIZE = 2; publiskais statiskais fināls int CHAR_FIELD_SIZE = 2; public static final int BYTE_FIELD_SIZE = 1; public static final int BOOLEAN_FIELD_SIZE = 1; public static final int DOUBLE_FIELD_SIZE = 8; publiskais statiskais fināls int FLOAT_FIELD_SIZE = 4; 

(Ir svarīgi apzināties, ka šīs konstantes nav uz visiem laikiem kodētas un tās ir neatkarīgi jāmēra konkrētam JVM.) Protams, naiva objektu lauku izmēru summēšana atstāj novārtā atmiņas izlīdzināšanas problēmas JVM. Atmiņas izlīdzināšanai ir nozīme (kā parādīts, piemēram, primitīvu masīvu tipiem Java Tip 130), taču es domāju, ka nav izdevīgi vajāt pēc šādām zema līmeņa detaļām. Šādas detaļas ir atkarīgas ne tikai no JVM piegādātāja, bet arī nav programmētāja kontrolē. Mūsu mērķis ir iegūt labu minējumu par objekta lielumu un, cerams, iegūt pavedienu, kad klases lauks varētu būt lieks; vai kad lauks būtu jāapdzīvo slinki; vai kad nepieciešama kompaktāka ligzdota datu struktūra utt. Lai iegūtu absolūtu fizisko precizitāti, jūs vienmēr varat atgriezties pie Sizeof klase Java Padomē 130.

Lai palīdzētu profilēt objekta gadījumu, mūsu rīks ne tikai aprēķinās izmēru, bet arī izveidos noderīgu datu struktūru kā blakusproduktu: diagrammu, kas sastāv no IObjectProfileNodes:

interfeiss IObjectProfileNode {Object object (); Stīgas nosaukums (); int lielums (); int pārrēķins (); IObjectProfileNode vecāks (); IObjectProfileNode [] bērni (); IObjectProfileNode apvalks (); IObjectProfileNode [] ceļš (); IObjectProfileNode sakne (); int ceļa garums (); būla šķērsojums (INodeFilter filtrs, INodeVisitor apmeklētājs); Stīgu izgāztuve (); } // saskarnes beigas 

IObjectProfileNodes ir savstarpēji savienoti gandrīz tieši tāpat kā sākotnējais objekta grafiks ar IObjectProfileNode.object () atgriežot reālo objektu, kuru katrs mezgls attēlo. IObjectProfileNode.size () atgriež objekta apakškoka kopējo izmēru (baitos), kas sakņojas šī mezgla objekta instancē. Ja objekta eksemplārs saista ar citiem objektiem, izmantojot nenullu instanču laukus vai ar atsaucēm masīva laukos, tad IObjectProfileNode.children () būs atbilstošs pakārtoto diagrammu mezglu saraksts, kas sakārtots samazinošā izmēra secībā. Un otrādi, katram mezglam, izņemot sākuma mezglu, IObjectProfileNode.parent () atgriež vecāku. Visa kolekcija IObjectProfileNodes tādējādi sagriež un sagriež sākotnējo objektu un parāda, kā tajā tiek sadalīta datu krātuve. Turklāt grafa mezglu nosaukumi tiek atvasināti no klases laukiem un tiek pārbaudīti mezgla ceļi grafikā (IObjectProfileNode.path ()) ļauj izsekot īpašumtiesību saites no sākotnējā objekta gadījuma līdz jebkuram iekšējam datu gabalam.

Jūs, iespējams, pamanījāt, lasot iepriekšējo rindkopu, ka līdzšinējā ideja joprojām ir neskaidra. Ja, šķērsojot objekta diagrammu, jūs vairākkārt sastopat vienu un to pašu objekta gadījumu (t.i., uz to norāda vairāk nekā viens lauks kaut kur diagrammā), kā jūs piešķirat tā īpašumtiesības (vecāku rādītājs)? Apsveriet šo koda fragmentu:

 Object obj = jauna virkne [] {jauna virkne ("JavaWorld"), jauna virkne ("JavaWorld")}; 

Katrs java.lang.Strings instancei ir tipa iekšējais lauks char [] tas ir faktiskais virknes saturs. Veids Stīga kopiju konstruktors darbojas gan Java 2 Platform, Standard Edition (J2SE) 1.4, gan abos Stīga gadījumi, kas atrodas iepriekšminētā masīva iekšienē, dalīsies tāpat char [] masīvs, kas satur {'J', 'a', 'v', 'a', 'W', 'o', 'r', 'l', 'd'} rakstzīmju secība. Abām stīgām šis masīvs pieder vienādi, un kas jums jādara šādos gadījumos?

Ja es vienmēr gribu grafa mezglam piešķirt vienu vecāku, tad šai problēmai nav universāli ideālas atbildes. Tomēr praksē daudzus šādus objektu gadījumus varēja izsekot vienam "dabiskam" vecākam. Šāda dabiska saišu secība parasti ir īsāks nekā citi, vairāk aprites maršruti. Padomājiet par datiem, uz kuriem norāda instances lauki, kas pieder vairāk šai instancei nekā jebkuram citam. Padomājiet par masīva ierakstiem, kas vairāk pieder pie šī masīva. Tādējādi, ja iekšējā objekta instanci var sasniegt pa vairākiem ceļiem, mēs izvēlamies īsāko ceļu. Ja mums ir vairāki vienāda garuma ceļi, mēs vienkārši izvēlamies pirmo atklāto. Sliktākajā gadījumā tā ir tikpat laba vispārēja stratēģija kā jebkura cita.

Domājot par grafu šķērsošanu un īsākajiem ceļiem, šajā brīdī vajadzētu noskanēt zvana signālam: platuma pirmais meklējums ir grafa šķērsošanas algoritms, kas garantē īsākā ceļa atrašanu no sākuma mezgla līdz jebkuram citam sasniedzamam grafa mezglam.

Pēc visiem šiem priekšdarbiem šeit ir šāda grafika šķērsošanas ieviešana mācību grāmatā. (Daži dati un palīgmetodes ir izlaistas; pilnīgu informāciju skatiet šī raksta lejupielādē.):

$config[zx-auto] not found$config[zx-overlay] not found