Programavimas

„Java“ dydis

2003 m. Gruodžio 26 d

Klausimas: Ar „Java“ turi tokį operatorių kaip sizeof () C?

A: Paviršutiniškai atsakoma, kad „Java“ nepateikia nieko panašaus į „C“ dydis(). Vis dėlto apsvarstykime kodėl „Java“ programuotojas kartais to gali norėti.

C programuotojas pats valdo daugumą duomenų struktūros atminties paskirstymų ir dydis() yra būtinas norint žinoti atminties blokų dydžius paskirstyti. Be to, C atminties paskirstytojams patinka malloc () beveik nieko nedarykite, kiek tai susiję su objekto inicijavimu: programuotojas turi nustatyti visus objektų laukus, kurie rodo kitų objektų rodiklius. Bet kai viskas pasakyta ir užkoduota, C / C ++ atminties paskirstymas yra gana efektyvus.

Palyginimui, „Java“ objektų paskirstymas ir konstravimas yra susieti (neįmanoma naudoti paskirto, bet neinicijuoto objekto egzemplioriaus). Jei „Java“ klasė apibrėžia laukus, kurie yra nuorodos į kitus objektus, taip pat įprasta juos nustatyti statybos metu. Todėl paskirstant „Java“ objektą, dažnai priskiriama daugybė tarpusavyje susijusių objektų egzempliorių: objekto grafikas. Kartu su automatiniu šiukšlių surinkimu tai yra pernelyg patogu ir gali jaustis tarsi niekada nereikėtų jaudintis dėl „Java“ atminties paskirstymo informacijos.

Žinoma, tai tinka tik paprastoms „Java“ programoms. Palyginti su C / C ++, lygiavertės „Java“ duomenų struktūros dažniausiai užima daugiau fizinės atminties. Kuriant įmonės programinę įrangą, artėjimas prie maksimalios virtualios atminties šiandieniniuose 32 bitų JVM yra įprastas mastelio apribojimas. Taigi „Java“ programuotojui tai galėtų būti naudinga dydis() ar kažkas panašaus, kad būtų galima stebėti, ar jo duomenų struktūros tampa per didelės, ar jose nėra atminties trūkumų. Laimei, „Java“ atspindys leidžia gana lengvai parašyti tokį įrankį.

Prieš tęsdamas atsisakysiu kelių, bet neteisingų atsakymų į šio straipsnio klausimą.

Klaida: „Sizeof“ () nereikia, nes „Java“ pagrindinių tipų dydžiai yra fiksuoti

Taip, „Java“ tarpt yra 32 bitai visose JVM ir visose platformose, tačiau tai yra tik kalbos specifikacijos reikalavimas suprantamas programuotoju šio tipo duomenų plotis. Toks tarpt iš esmės yra abstraktus duomenų tipas ir gali būti paremtas, tarkime, 64 bitų fizinės atminties žodžiu 64 bitų mašinoje. Tas pats pasakytina ir apie neprimityvius tipus: „Java“ kalbos specifikacijoje nieko nepasakyta apie tai, kaip klasiniai laukai turėtų būti sulyginti fizinėje atmintyje, ar kad booleanų masyvo negalima įdiegti kaip kompaktišką bitvektorių JVM viduje.

Klaidingumas: Galite išmatuoti objekto dydį, sustatydami jį į baitų srautą ir žiūrėdami į gautą srauto ilgį

Priežastis, kad tai neveikia, yra ta, kad serijos išdėstymas yra tik nuotolinis tikrojo atminties išdėstymo atspindys. Vienas paprastas būdas tai pamatyti yra pažiūrėti, kaip Stygoss serijos: atmintyje kas char yra mažiausiai 2 baitai, bet serijine forma Stygosjos yra UTF-8 koduojamos, todėl bet koks ASCII turinys užima perpus mažiau vietos.

Kitas darbinis požiūris

Galite prisiminti „Java Patarimas 130: Ar žinote savo duomenų dydį?“ aprašyta technika, pagrįsta daugybės vienodų klasės egzempliorių sukūrimu ir kruopščiai matuojant gautą JVM naudojamo krūvos dydžio padidėjimą. Kai taikoma, ši idėja veikia labai gerai, ir aš iš tikrųjų ją naudosiu, norėdamas paleisti šiame straipsnyje pateiktą alternatyvų metodą.

Atminkite, kad „Java Tip 130“ Dydis klasei reikia ramybės būsenoje esančio JVM (kad krūvos veikla būtų vykdoma tik dėl objektų paskirstymo ir šiukšlių surinkimo, kurių reikalauja matavimo gija) ir reikalauja daugybės vienodų objektų egzempliorių. Tai neveikia, kai norite nustatyti vieno didelio objekto dydį (galbūt kaip derinimo pėdsakų išvesties dalį) ir ypač tada, kai norite ištirti, kas iš tikrųjų jį padarė tokį didelį.

Koks yra objekto dydis?

Aukščiau pateiktoje diskusijoje išryškėja filosofinė mintis: atsižvelgiant į tai, kad jūs dažniausiai susiduriate su objekto grafikais, koks yra objekto dydžio apibrėžimas? Ar tai tik nagrinėjamo objekto egzemplioriaus dydis, ar viso duomenų grafiko, įsišaknijusio objekto egzemplioriuje, dydis? Pastarasis yra tai, kas paprastai yra svarbiau praktikoje. Kaip pamatysite, ne visada viskas yra taip aišku, tačiau pradedantiesiems galite vadovautis šiuo požiūriu:

  • Objekto egzempliorius gali būti (apytiksliai) dydžio susumavus visus jo nestatiškus duomenų laukus (įskaitant laukus, apibrėžtus superklase)
  • Skirtingai nei, tarkime, C ++, klasės metodai ir jų virtualumas neturi įtakos objekto dydžiui
  • Klasės superinterfai neturi įtakos objekto dydžiui (žr. Pastabą šio sąrašo pabaigoje)
  • Visą objekto dydį galima gauti kaip uždarymą visam objekto grafikui, įsišaknijusiam pradiniame objekte
Pastaba: Įdiegus bet kurią „Java“ sąsają, tik pažymima atitinkama klasė ir prie jos apibrėžimo nepateikiami jokie duomenys. Tiesą sakant, JVM net nepatvirtina, kad sąsajos diegimas suteikia visus sąsajai reikalingus metodus: tai yra griežtai kompiliatoriaus atsakomybė pagal dabartines specifikacijas.

Norėdami paleisti procesą, primityviems duomenų tipams naudoju fizinius dydžius, matuojamus pagal „Java Tip 130“ Dydis klasė. Kaip paaiškėja, įprastiems 32 bitų JVM paprastas java.lang.Object užima 8 baitus, o pagrindiniai duomenų tipai paprastai yra mažiausio fizinio dydžio, galintys atitikti kalbos reikalavimus (išskyrus loginis užima visą baitą):

 // java.lang. Objekto apvalkalo dydis baitais: public static final int OBJECT_SHELL_SIZE = 8; public static final int OBJREF_SIZE = 4; viešasis statinis finalas LONG_FIELD_SIZE = 8; public static final int INT_FIELD_SIZE = 4; public static final int SHORT_FIELD_SIZE = 2; viešasis statinis finalas 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; public static final int FLOAT_FIELD_SIZE = 4; 

(Svarbu suvokti, kad šios konstantos nėra amžinai užkoduotos ir jas reikia atskirai matuoti pagal tam tikrą JVM.) Žinoma, naivus objektų laukų dydžių sumavimas nepaiso atminties derinimo problemų JVM. Atminties lygiavimas turi reikšmės (kaip parodyta, pavyzdžiui, primityviems masyvų tipams „Java Tip 130“), bet manau, kad yra nepelninga vaikytis tokių žemo lygio detalių. Tokia informacija ne tik priklauso nuo JVM tiekėjo, bet ir nėra programuotojo kontroliuojama. Mūsų tikslas yra gerai atspėti objekto dydį ir, tikėkimės, sužinoti, kada klasės laukas gali būti nereikalingas; arba kai laukas turėtų būti tingiai apgyvendintas; arba kai reikalinga kompaktiškesnė įdėta duomenų struktūra ir kt. Norėdami pasiekti visišką fizinį tikslumą, visada galite grįžti į Dydis klasė „Java Tip 130“.

Norėdami padėti apibūdinti objekto egzempliorių, mūsų įrankis ne tik apskaičiuos dydį, bet ir sukurs naudingą duomenų struktūrą kaip šalutinį produktą: grafiką, sudarytą iš „IObjectProfileNode“s:

sąsaja IObjectProfileNode {Object object (); Stygos pavadinimas (); int dydis (); int refcount (); „IObjectProfileNode“ tėvas (); IObjectProfileNode [] vaikai (); „IObjectProfileNode“ apvalkalas (); IObjectProfileNode [] kelias (); IObjectProfileNode šaknis (); int kelio ilgis (); boolean traversas (INodeFilter filtras, INodeVisitor lankytojas); Styginių sąvartynas (); } // Sąsajos pabaiga 

„IObjectProfileNode“s yra tarpusavyje susieti beveik lygiai taip pat, kaip su pradiniu objekto grafiku „IObjectProfileNode.object“ () grąžinant tikrąjį objektą, kurį rodo kiekvienas mazgas. „IObjectProfileNode.size“ () pateikia bendrą objekto potemio, įsišaknijusio to mazgo objekto egzemplioriuje, dydį (baitais). Jei objekto egzempliorius susieja su kitais objektais per nenulinius egzempliorių laukus arba per nuorodas, esančias masyvo laukuose, tada IObjectProfileNode.children () bus atitinkamas antrinių diagramų mazgų sąrašas, surūšiuotas mažėjančia tvarka. Ir atvirkščiai, kiekvienam mazgui, išskyrus pradinį, „IObjectProfileNode.parent“ () grąžina savo tėvą. Visa kolekcija „IObjectProfileNode“Tokiu būdu supjaustomas ir supjaustomas originalus objektas ir parodoma, kaip jame yra padalijama duomenų saugykla. Be to, grafiko mazgų pavadinimai yra kilę iš klasės laukų ir tiriant mazgo kelią grafike („IObjectProfileNode.path“ ()) leidžia atsekti nuosavybės nuorodas nuo pradinio objekto egzemplioriaus iki bet kokio vidinio duomenų.

Skaitydami ankstesnę pastraipą galbūt pastebėjote, kad iki šiol idėja vis dar turi neaiškumų. Jei, eidami per objekto grafiką, su tuo pačiu objekto egzemplioriumi susiduriate daugiau nei vieną kartą (t. Y. Į jį nukreipia daugiau nei vienas laukas kur nors diagramoje), kaip priskirti jo nuosavybę (pagrindinį rodyklę)? Apsvarstykite šį kodo fragmentą:

 Object obj = nauja eilutė [] {nauja eilutė („JavaWorld“), nauja eilutė („JavaWorld“)}; 

Kiekvienas java.lang.Stringas egzempliorius turi vidinį tipo lauką char [] tai yra tikrasis eilutės turinys. Taip Stygos kopijų konstruktorius veikia „Java 2 Platform“, „Standard Edition“ (J2SE) 1.4 versijoje Stygos aukščiau esančio masyvo egzemplioriai bus tokie patys char [] masyvas, kuriame yra {'J', 'a', 'v', 'a', 'W', 'o', 'r', 'l', 'd'} simbolių seka. Ši masyvas vienodai priklauso abiem eilutėms, todėl ką turėtumėte daryti tokiais atvejais?

Jei aš visada noriu priskirti vieną tėvą prie grafiko mazgo, tai ši problema neturi visuotinai tobulo atsakymo. Tačiau praktikoje daugelį tokių objektų atvejų galima atsekti iš vieno „natūralaus“ tėvo. Tokia natūrali sąsajų seka dažniausiai yra trumpesnis nei kiti, labiau žiediniai maršrutai. Pagalvokite apie duomenis, nurodytus egzempliorių laukuose, labiau priklausančius tam egzemplioriui nei bet kam kitam. Pagalvokite apie masyvo įrašus, priklausančius daugiau tam masyvui. Taigi, jei vidinį objekto egzempliorių galima pasiekti keliais keliais, mes pasirenkame trumpiausią kelią. Jei turime kelis vienodo ilgio kelius, gerai, mes tiesiog pasirenkame pirmąjį atrastą. Blogiausiu atveju tai yra tokia pat gera bendra strategija kaip ir bet kuri kita.

Galvojant apie grafikų perėjimus ir trumpiausius kelius, šiuo metu turėtų skambėti varpas: „plotis pirmas“ yra grafiko perėjimo algoritmas, garantuojantis rasti trumpiausią kelią nuo pradinio mazgo iki kito pasiekiamo grafiko mazgo.

Po visų šių parengiamųjų darbų pateikiamas vadovo tokio grafiko perėjimo įgyvendinimas. (Kai kurios detalės ir pagalbiniai metodai buvo praleisti; išsamią informaciją rasite šio straipsnio atsisiuntime.):