Programiranje

Dokončanje in čiščenje predmetov

Pred tremi meseci sem začel mini serijo člankov o oblikovanju predmetov z razpravo o principih oblikovanja, ki so se osredotočili na pravilno inicializacijo na začetku življenja predmeta. V tem Tehnike oblikovanja V članku se bom osredotočil na principe oblikovanja, ki vam pomagajo zagotoviti pravilno čiščenje ob koncu življenjske dobe predmeta.

Zakaj pospravljati?

Vsak objekt v programu Java uporablja končne računalniške vire. Najbolj očitno je, da vsi predmeti uporabljajo nekaj pomnilnika za shranjevanje svojih slik na kup. (To velja tudi za predmete, ki ne razglasijo nobenih spremenljivk primerka. Vsaka slika predmeta mora vsebovati nekakšen kazalec na podatke razreda in lahko vključuje tudi druge informacije, ki so odvisne od izvedbe.) Toda predmeti lahko poleg pomnilnika uporabljajo tudi druge končne vire. Na primer, nekateri predmeti lahko uporabljajo vire, kot so ročaji datotek, grafični konteksti, vtičnice itd. Ko načrtujete objekt, se prepričajte, da sčasoma sprosti vse končne vire, ki jih uporablja, da jih sistem ne bo zmanjkalo.

Ker je Java jezik, ki se zbira v smeti, je sprostitev pomnilnika, povezanega s predmetom, enostavna. Vse, kar morate storiti, je, da opustite vse reference na predmet. Ker vam ni treba skrbeti za izrecno osvoboditev predmeta, tako kot v jezikih, kot sta C ali C ++, vam ni treba skrbeti, da bi poškodovali spomin tako, da bi dvakrat pomotoma osvobodili isti predmet. Vendar se morate prepričati, ali ste dejansko sprostili vse reference na predmet. Če tega ne storite, lahko na koncu pride do uhajanja pomnilnika, tako kot pri uhajanju pomnilnika v programu C ++, ko pozabite izrecno osvoboditi predmete. Kljub temu, da sprostite vse reference na predmet, vam ni treba skrbeti, da boste ta spomin izrecno "osvobodili".

Podobno vam ni treba skrbeti za izrecno osvoboditev sestavnih predmetov, na katere se sklicujejo spremenljivke primerka predmeta, ki ga ne potrebujete več. Sprostitev vseh sklicev na nepotrebni objekt bo dejansko razveljavila vse sklice na sestavne predmete, ki jih vsebujejo spremenljivke primerka tega predmeta. Če so bile zdaj razveljavljene reference edine preostale reference na te sestavne predmete, bodo sestavni predmeti na voljo tudi za odvoz smeti. Kos torte, kajne?

Pravila odvoza smeti

Čeprav zbiranje smeti resnično olajša upravljanje pomnilnika v Javi kot v C ali C ++, pri programiranju v Javi ne morete popolnoma pozabiti na pomnilnik. Če želite vedeti, kdaj boste morda morali razmisliti o upravljanju pomnilnika v Javi, morate vedeti nekaj o tem, kako se zbiranje smeti obravnava v specifikacijah Java.

Odvoz smeti ni obvezen

Najprej morate vedeti, da ne glede na to, kako vestno iščete po specifikaciji Java Virtual Machine (JVM Spec), ne boste mogli najti nobenega stavka, ki bi ukazoval, Vsak JVM mora imeti zbiralnik smeti. Specifikacija navideznega računalnika Java daje oblikovalcem VM veliko prostora pri odločanju, kako bodo njihove izvedbe upravljale pomnilnik, vključno z odločitvijo, ali sploh uporabiti zbiranje smeti ali ne. Tako je mogoče, da bodo nekateri JVM-ji (na primer JVM pametne kartice z golimi kostmi) zahtevali, da se programi, ki se izvajajo v vsaki seji, "prilegajo" v razpoložljivi pomnilnik.

Seveda vam lahko vedno zmanjka pomnilnika, tudi v sistemu navideznega pomnilnika. V specifikaciji JVM ni navedeno, koliko pomnilnika bo na voljo JVM. Navaja le, da kadar koli JVM naredi zmanjkalo spomina, naj vrže OutOfMemoryError.

Kljub temu pa ima večina JVM za zbiranje smeti večino možnosti za izvajanje Java-programov najboljše možnosti za izvajanje, ne da bi vam zmanjkalo pomnilnika. Zbiralnik smeti povrne pomnilnik, ki ga zasedajo nepremični predmeti na kupu, tako da lahko novi predmeti znova uporabijo pomnilnik in ponavadi defragmentira kup med izvajanjem programa.

Algoritem za odvoz smeti ni definiran

Še en ukaz, ki ga ne najdete v specifikaciji JVM, je Vsi JVM-ji, ki uporabljajo odvoz smeti, morajo uporabljati algoritem XXX. Oblikovalci vsakega JVM se sami odločijo, kako bo zbiranje smeti delovalo v njihovih izvedbah. Algoritem za odvoz smeti je eno področje, na katerem si lahko ponudniki JVM prizadevajo, da bi bilo njihovo izvajanje boljše od konkurence. To je za vas kot programerja Java pomembno iz naslednjega razloga:

Ker na splošno ne veste, kako se bo zbiranje smeti izvajalo znotraj JVM, ne veste, kdaj bo kateri koli predmet zbran.

Pa kaj? boste morda vprašali. Razlog, da vas morda zanima, če je predmet zbrano smeti, je povezan s finalizatorji. (A finalizator je definiran kot običajna metoda primerka Java z imenom dokončaj () ki vrne void in ne sprejme nobenega argumenta.) Specifikacije Java dajejo naslednje obljube glede zaključevalnikov:

Preden povrne pomnilnik, ki ga zaseda objekt, ki ima zaključevalnik, bo zbiralec smeti priklical zaključevalnik tega predmeta.

Glede na to, da ne veste, kdaj bodo predmeti zbrani smeti, vendar veste, da bodo dokončni predmeti dokončani, saj so zbrani smeti, lahko naredite naslednji velik odbitek:

Ne veste, kdaj bodo predmeti dokončani.

To pomembno dejstvo bi morali vtisniti v svoje možgane in mu za vedno dovoliti, da obvešča o načrtih vaših predmetov Java.

Finalizatorjem, ki se jim je treba izogniti

Osrednje pravilo glede finalizatorjev je naslednje:

Programov Java ne oblikujte tako, da bo pravilnost odvisna od "pravočasne" dokončnosti.

Z drugimi besedami, ne pišite programov, ki se bodo zlomili, če določeni predmeti ne bodo dokončani do določenih točk v življenju programa. Če napišete tak program, bo morda deloval pri nekaterih izvedbah JVM, pri drugih pa ne.

Ne zanašajte se na zaključevalnike, da sprostijo vire, ki niso pomnilniški

Primer predmeta, ki krši to pravilo, je tisti, ki odpre datoteko v konstruktorju in datoteko v svojem dokončaj () metoda. Čeprav se zdi ta oblika urejena, urejena in simetrična, lahko ustvari zahrbtno napako. Program Java bo na splošno imel na voljo le končno število ročajev datotek. Ko so vsi ti ročaji v uporabi, program ne bo mogel več odpirati datotek.

Program Java, ki uporablja tak objekt (tisti, ki odpre datoteko v konstruktorju in jo zapre v zaključevalniku), bo morda dobro deloval pri nekaterih izvedbah JVM. Pri takih izvedbah bi se finalizacija zgodila dovolj pogosto, da bi bilo ves čas na voljo zadostno število ročajev datotek. Toda isti program morda ne bo uspel na drugem JVM, katerega zbiralnik smeti ne dokonča dovolj pogosto, da programu ne bi zmanjkalo ročajev datotek. Ali, kar je še bolj zahrbtno, program lahko zdaj deluje na vseh izvedbah JVM, vendar nekaj let (in izpustni cikli) po cesti ne uspe.

Druga pravila zaključevalnika

Dve drugi odločitvi, ki sta bili prepuščeni oblikovalcem JVM, sta izbira niti (ali niti), ki bo izvedla zaključevalnike, in vrstni red izvajanja zaključevalnikov. Dokončniki se lahko izvajajo v poljubnem vrstnem redu - zaporedno z eno niti ali hkrati z več nitmi. Če je vaš program nekako odvisen od pravilnosti zaključnih programov, ki se izvajajo v določenem vrstnem redu ali z določeno nitjo, lahko deluje pri nekaterih izvedbah JVM, pri drugih pa ne.

Upoštevati morate tudi, da Java meni, da je predmet dokončan, ne glede na to, ali je dokončaj () metoda se vrne normalno ali nenadoma dokonča z vrnitvijo izjeme. Zbiralci smeti prezrejo kakršne koli izjeme, ki jih vržejo zaključevalniki, in nikakor ne obvestijo ostale aplikacije, da je bila vržena izjema. Če želite zagotoviti, da določen zaključevalec v celoti izpolni določeno nalogo, ga morate napisati tako, da bo obravnaval kakršne koli izjeme, ki se lahko pojavijo, preden zaključevalec izpolni svojo nalogo.

Še eno osnovno pravilo o zaključevalcih se nanaša na predmete, ki ostanejo na kopici ob koncu življenjske dobe aplikacije. Privzeto zbiralnik smeti ne zažene zaključevalnih elementov nobenega predmeta, ki je ostal na kupu, ko aplikacija zapre. Če želite spremeniti to privzeto vrednost, morate uporabiti runFinalizersOnExit () metoda pouka Izvajanje ali Sistem, mimo prav kot en parameter. Če vaš program vsebuje predmete, katerih zaključnike je treba nujno priklicati pred izhodom programa, ga obvezno pokličite runFinalizersOnExit () nekje v vašem programu.

Za kaj so torej dokončniki dobri?

Zdaj imate morda občutek, da vam finalizatorji nimajo veliko koristi. Čeprav je verjetno, da večina razredov, ki jih načrtujete, ne vključuje zaključevalnika, obstaja nekaj razlogov za uporabo zaključevalnikov.

Ena razumna, čeprav redka aplikacija za dokončanje je sprostitev pomnilnika, dodeljenega z izvornimi metodami. Če objekt prikliče izvorno metodo, ki dodeli pomnilnik (morda funkcija C, ki prikliče malloc ()), lahko zaključevalec tega predmeta prikliče izvorno metodo, ki sprosti ta pomnilnik (klici prost()). V tem primeru bi s pomočjo zaključevalnika sprostili pomnilnik, dodeljen v imenu predmeta - pomnilnika, ki ga zbiralec smeti ne bo samodejno povrnil.

Druga, bolj pogosta uporaba zaključevalnikov je zagotavljanje nadomestnega mehanizma za sproščanje nespomnilniških končnih virov, kot so datotečni ročaji ali vtičnice. Kot smo že omenili, se pri sprostitvi končnih virov, ki niso v pomnilniku, ne bi smeli zanašati na zaključevalce. Namesto tega morate navesti metodo, ki bo sprostila vir. Morda pa boste želeli vključiti tudi zaključevalnik, ki preveri, ali je vir že izdan, in če ni, nadaljuje in ga sprosti. Tak zaključevalnik ščiti pred (in upajmo, da ne bo spodbudil) površno uporabo vašega razreda. Če odjemalski programer pozabi priklicati metodo, ki ste jo navedli za sprostitev vira, bo finalizator izpustil vir, če je predmet kdaj zbran smeti. The dokončaj () metoda LogFileManager razred, prikazan kasneje v tem članku, je primer tovrstnega zaključevalnika.

Izogibajte se zlorabi finalizatorja

Obstoj dokončanja ustvarja nekaj zanimivih zapletov za JVM in nekaj zanimivih možnosti za programerje Java. Programerjem dokončno dodeli moč nad življenjem in smrtjo predmetov. Skratka, v Javi je mogoče popolnoma zakonito obuditi predmete v zaključnikih - jih oživiti, tako da se znova sklicujejo nanje. (Eden od načinov, kako bi to lahko dosegel zaključek, je dodajanje sklica na predmet, ki je dokončan, na statični povezani seznam, ki je še vedno "v živo".) Čeprav je takšna moč morda skušnjava za izvajanje, ker se počutite pomembnega, pravilo je upreti se skušnjavi, da bi uporabili to moč. Na splošno oživitev predmetov v zaključevalnih programih pomeni zlorabo zaključnega programa.

Glavna utemeljitev tega pravila je, da je vsak program, ki uporablja vstajenje, mogoče preoblikovati v lažje razumljiv program, ki ne uporablja vstajenja. Formalni dokaz tega izreka je bralcu prepuščen (to sem vedno želel reči), vendar v neformalnem duhu pomislite, da bo vstajenje predmetov tako naključno in nepredvidljivo kot dokončanje predmeta. Kot tak bo načrt, ki uporablja vstajenje, težko našel naslednji vzdrževalec, ki se bo zgodil - ki morda ne bo popolnoma razumel posebnosti zbiranja smeti v Javi.

Če menite, da morate predmet preprosto oživiti, razmislite o kloniranju nove kopije predmeta, namesto da bi obudili isti stari predmet. Utemeljitev tega nasveta je, da se zbiralci smeti v JVM sklicujejo na dokončaj () metoda predmeta samo enkrat. Če ta predmet vstane in postane drugič na voljo za odvoz smeti, predmet dokončaj () metoda ne bo več uporabljena.

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