Programmēšana

JVM veiktspējas optimizācija, 3. daļa: Atkritumu savākšana

Java platformas atkritumu savākšanas mehānisms ievērojami palielina izstrādātāju produktivitāti, taču slikti ieviests atkritumu savācējs var pārmērīgi patērēt lietojumprogrammu resursus. Šajā trešajā rakstā JVM veiktspējas optimizācija sērijā Eva Andreasson piedāvā Java iesācējiem pārskatu par Java platformas atmiņas modeli un GC mehānismu. Pēc tam viņa paskaidro, kāpēc sadrumstalotība (nevis GC) ir galvenā "gotcha!" Java lietojumprogrammu veiktspēju, un kāpēc paaudžu atkritumu savākšana un blīvēšana šobrīd ir vadošā (lai arī ne visnovatoriskākā) pieeja kaudzes sadrumstalotības pārvaldībai Java lietojumprogrammās.

Atkritumu kolekcija (GC) ir process, kura mērķis ir atbrīvot aizņemto atmiņu, uz kuru vairs neatsaucas neviens sasniedzams Java objekts, un tas ir būtiska Java virtuālās mašīnas (JVM) dinamiskās atmiņas pārvaldības sistēmas sastāvdaļa. Tipiskā atkritumu savākšanas ciklā tiek glabāti visi objekti, uz kuriem joprojām ir atsauce, un tādējādi tie ir sasniedzami. Vieta, kuru aizņem iepriekš norādītie objekti, tiek atbrīvota un atgūta, lai iespējotu jaunu objektu piešķiršanu.

Lai saprastu atkritumu savākšanu un dažādas GC pieejas un algoritmus, vispirms jāzina dažas lietas par Java platformas atmiņas modeli.

JVM veiktspējas optimizācija: izlasiet sēriju

  • 1. daļa: Pārskats
  • 2. daļa: Kompilatori
  • 3. daļa: Atkritumu savākšana
  • 4. daļa: Vienlaicīgi blīvējošs GC
  • 5. daļa: Mērogojamība

Atkritumu savākšana un Java platformas atmiņas modelis

Norādot startēšanas opciju -Xmx Java lietojumprogrammas komandrindā (piemēram: java -Xmx: 2g MyApp) Java procesam tiek piešķirta atmiņa. Šī atmiņa tiek dēvēta par Java kaudze (vai vienkārši kaudze). Šī ir atvēlētā atmiņas adreses vieta, kur tiks piešķirti visi jūsu Java programmas (vai dažreiz JVM) izveidotie objekti. Kad jūsu Java programma darbojas un piešķir jaunus objektus, Java kaudze (tas nozīmē, ka adreses telpa) tiks piepildīta.

Galu galā Java kaudze būs pilna, kas nozīmē, ka piešķirošais pavediens nespēj atrast pietiekami lielu secīgu brīvās atmiņas sadaļu objektam, kuru viņš vēlas piešķirt. Tajā brīdī JVM nosaka, ka jānotiek atkritumu savākšanai, un par to paziņo atkritumu savācējam. Atkritumu savākšanu var aktivizēt arī tad, kad zvana Java programma System.gc (). Izmantojot System.gc () negarantē atkritumu savākšanu. Pirms sākt atkritumu savākšanu, GC mehānisms vispirms noteiks, vai to ir droši sākt. Ir droši sākt atkritumu savākšanu, kad visi lietojumprogrammas aktīvie pavedieni ir drošā vietā, lai to atļautu, piem. vienkārši paskaidroja, ka būtu slikti sākt atkritumu savākšanu notiekoša objekta sadalījuma vidū vai optimizētu CPU instrukciju secības izpildes vidū (skatiet manu iepriekšējo rakstu par kompilatoriem), jo jūs varat zaudēt kontekstu un tādējādi sajaukt galu rezultātiem.

Atkritumu savācējam vajadzētu nekad atgūt objektu, uz kuru aktīvi atsaucas; lai to izdarītu, tiktu pārkāpta Java virtuālās mašīnas specifikācija. Atkritumu savācējam arī nav pienākums nekavējoties savākt mirušos priekšmetus. Nogalinātie objekti tiek savākti turpmāko atkritumu savākšanas ciklu laikā. Lai gan ir daudz veidu, kā īstenot atkritumu savākšanu, šie divi pieņēmumi attiecas uz visām šķirnēm. Atkritumu savākšanas patiesais izaicinājums ir identificēt visu, kas ir aktīvs (joprojām ir atsauce), un atgūt jebkuru nereģistrēto atmiņu, taču dariet to, neietekmējot darbojošās lietojumprogrammas vairāk nekā nepieciešams. Tādējādi atkritumu savācējam ir divi mandāti:

  1. Lai ātri atbrīvotu nereferēto atmiņu, lai apmierinātu lietojumprogrammas piešķiršanas ātrumu, lai tai nepietrūktu atmiņas.
  2. Atgūt atmiņu, vienlaikus minimāli ietekmējot darbojošās lietojumprogrammas veiktspēju (piemēram, latentumu un caurlaidspēju).

Divu veidu atkritumu savākšana

Pirmajā šīs sērijas rakstā es pieskāros divām galvenajām pieejām atkritumu savākšanai, kas ir atsauces skaitīšana un savācēju izsekošana. Šoreiz es sīkāk izpētīšu katru pieeju, pēc tam iepazīstināšu ar dažiem algoritmiem, kas izmantoti kolektoru izsekošanas ieviešanai ražošanas vidēs.

Izlasiet JVM veiktspējas optimizācijas sēriju

  • JVM veiktspējas optimizācija, 1. daļa: Pārskats
  • JVM veiktspējas optimizācija, 2. daļa: Kompilatori

Atskaites skaitīšanas kolektori

Atskaites skaitītāji sekojiet līdzi tam, cik daudz atsauču norāda uz katru Java objektu. Kad objekta skaits kļūst nulle, atmiņu var nekavējoties atgūt. Šī tūlītēja piekļuve atjaunotajai atmiņai ir galvenā atsauces uzskaites pieejas priekšrocība atkritumu savākšanā. Ir ļoti maz pieskaitāmo līdzekļu, ja nepieciešams turēt atmiņā bez atsauces. Visu atsauču skaita atjaunināšana tomēr var būt diezgan dārga.

Galvenās grūtības, kas saistītas ar atskaites skaitītāju kolekcionāriem, ir precīzas atskaites skaitīšanas nodrošināšana. Vēl viens labi zināms izaicinājums ir sarežģītība, kas saistīta ar apļveida struktūru apstrādi. Ja divi objekti atsaucas viens uz otru un neviens dzīvs objekts uz tiem neattiecas, viņu atmiņa nekad netiks atbrīvota. Abi objekti uz visiem laikiem paliks ar skaitli, kas nav nulle. Lai atgūtu atmiņu, kas saistīta ar apļveida struktūrām, ir jāveic nopietna analīze, kas algoritmam un līdz ar to arī lietojumprogrammai rada dārgas izmaksas.

Kolektoru izsekošana

Kolektoru izsekošana ir balstīti uz pieņēmumu, ka visus dzīvos objektus var atrast, iteratīvi izsekojot visas atsauces un turpmākās atsauces no sākotnēji zināmiem kā dzīviem objektiem. Sākotnējais dzīvo objektu kopums (saukts saknes objekti vai vienkārši saknes īsāk sakot) atrodas, analizējot reģistrus, globālos laukus un kaudzes rāmjus brīdī, kad tiek aktivizēta atkritumu savākšana. Pēc sākotnējā tiešraides kopas noteikšanas izsekotāju kolekcionārs seko atsaucēm uz šiem objektiem un rindā tos iezīmē kā dzīvus un pēc tam izseko viņu atsauces. Atzīmē visus atrastos atsauces objektus tiešraide nozīmē, ka zināmā tiešraides kopa laika gaitā palielinās. Šis process turpinās, līdz tiek atrasti un atzīmēti visi atsauces (un līdz ar to visi dzīvie) objekti. Kad izsekošanas kolekcionārs ir atradis visus dzīvos objektus, tas atgūs atlikušo atmiņu.

Izsekošanas kolektori atšķiras no atskaites kolektoriem, jo ​​tie var rīkoties ar apļveida konstrukcijām. Lielākā daļa izsekotāju savācēju ir marķēšanas fāze, kas prasa gaidīšanu, pirms varēs atgūt atmiņu, uz kuru nav atsauces.

Trasēšanas kolekcionāri visbiežāk tiek izmantoti atmiņas pārvaldībai dinamiskās valodās; tie neapšaubāmi ir visizplatītākie Java valodā un jau daudzus gadus ir komerciāli pierādīti ražošanas vidēs. Es koncentrēšos uz kolekcionāru izsekošanu atlikušajā šī raksta daļā, sākot ar dažiem algoritmiem, kas ievieš šo pieeju atkritumu savākšanai.

Kolektoru algoritmu izsekošana

Kopēšana un atzīmēt un slaucīt atkritumu savākšana nav jauna, taču tie joprojām ir divi izplatītākie algoritmi, kas mūsdienās ievieš atkritumu savākšanas izsekošanu.

Kolekcionāru kopēšana

Tradicionālie kopēšanas kolekcionāri izmanto a no kosmosa un a kosmosā - tas ir, divas atsevišķi definētas kaudzes adrešu telpas. Atkritumu savākšanas brīdī dzīvie objekti apgabalā, kas definēts kā no kosmosa, tiek kopēti nākamajā pieejamajā vietā apgabalā, kas definēts kā kosmosa. Kad visi dzīvie objekti no kosmosa tiek pārvietoti ārā, visu no kosmosa var atgūt. Kad piešķiršana sākas no jauna, tā sākas no pirmās brīvās vietas kosmosā.

Vecākos šī algoritma ieviešanas gadījumos no kosmosa un uz kosmosu tiek pārslēgtas vietas, tas nozīmē, ka tad, kad kosmoss ir pilns, atkritumu savākšana atkal tiek aktivizēta un telpa kļūst par kosmosu, kā parādīts 1. attēlā.

Mūsdienīgākas kopēšanas algoritma ieviešanas ļauj patvaļīgas adrešu vietas kaudzē piešķirt kā atstarpi un no kosmosa. Šādos gadījumos viņiem nav obligāti jāmaina atrašanās vieta savā starpā; drīzāk katrs no tiem kļūst par citu adreses vietu kaudzē.

Viena kolekcionāru kopēšanas priekšrocība ir tā, ka objekti tiek cieši izvietoti kosmosā, pilnībā novēršot sadrumstalotību. Sadrumstalotība ir izplatīta problēma, ar kuru cīnās citi atkritumu savākšanas algoritmi; kaut ko es apspriedīšu vēlāk šajā rakstā.

Kolektoru kopēšanas negatīvās puses

Kopētāji parasti ir stop-the-world kolekcionāri, kas nozīmē, ka nevienu lietojumprogrammas darbu nevar veikt tik ilgi, kamēr atkritumu savākšana notiek ciklā. Īstenojot pasaules pieturas režīmu, jo lielāka platība ir jākopē, jo lielāka būs jūsu lietojumprogrammas veiktspējas ietekme. Tas ir trūkums lietojumprogrammām, kas ir jutīgas pret reakcijas laiku. Izmantojot kopēšanas kolekcionāru, jums jāņem vērā arī vissliktākais scenārijs, kad viss notiek no kosmosa. Jums vienmēr ir jāatstāj pietiekami daudz vietas, lai pārvietotu dzīvus objektus, kas nozīmē, ka atstarpei jābūt pietiekami lielai, lai tajā varētu izvietot visu, kas atrodas no kosmosa. Kopēšanas algoritms ir nedaudz neefektīvs atmiņā šī ierobežojuma dēļ.

Atzīmēšanas un slaucīšanas kolekcionāri

Lielākā daļa uzņēmuma ražošanas vidē izvietoto komerciālo JVM darbina marķēšanas un slaucīšanas (vai marķēšanas) kolekcionārus, kuriem nav tādas ietekmes uz darbību kā kopēšanas kolekcionāru ietekmei. Daži no slavenākajiem marķējumu kolekcionāriem ir CMS, G1, GenPar un DeterministicGC (skatīt resursus).

A atzīmju un slaucīšanas kolekcionārs izseko atsauces un atzīmē katru atrasto objektu ar “dzīvu” bitu. Parasti kopas bits atbilst adresei vai dažos gadījumos adrešu kopai uz kaudzes. Dzīvo bitu, piemēram, var saglabāt kā bitu objekta galvenē, bitu vektorā vai bitu kartē.

Pēc tam, kad viss ir atzīmēts tiešraidē, sāksies slaucīšanas fāze. Ja kolekcionāram ir slaucīšanas fāze, tas būtībā ietver kādu mehānismu, lai atkal pārvietotos ar kaudzi (ne tikai dzīvu kopu, bet arī visu kaudzes garumu), lai atrastu visus nepiezīmētos. secīgu atmiņas adrešu atstarpju gabali. Nepiezīmēta atmiņa ir brīva un to var atgūt. Tad kolekcionārs sasaista šos nemarķētos gabalus organizētos bezmaksas sarakstos. Atkritumu savācējā var būt dažādi bezmaksas saraksti - parasti sakārtoti pēc gabalu izmēriem. Daži JVM (piemēram, JRockit Real Time) ievāc kolektorus ar heiristiku, kas dinamiski palielina diapazonu sarakstus, pamatojoties uz lietojumprogrammu profilēšanas datiem un objekta lieluma statistiku.

Kad slaucīšanas fāze būs pabeigta, piešķiršana tiks atsākta no jauna. Jaunas piešķiršanas zonas tiek piešķirtas no brīvajiem sarakstiem, un atmiņas gabalus varētu saskaņot ar objektu izmēriem, objekta lieluma vidējiem rādītājiem uz pavedienu ID vai lietojumprogrammas pielāgotajiem TLAB izmēriem. Brīvās vietas precīzāka pielāgošana lietojumprogrammas izmēram optimizē atmiņu un var palīdzēt samazināt sadrumstalotību.

Vairāk par TLAB izmēriem

TLAB un TLA (Thread Local Allocation Buffer or Thread Local Area) nodalīšana ir apskatīta JVM veiktspējas optimizācijas 1. daļā.

Zīmju un slaucīšanas kolekcionāru negatīvās puses

Atzīmēšanas fāze ir atkarīga no jūsu kaudzes dzīvo datu apjoma, bet slaucīšanas fāze ir atkarīga no kaudzes lieluma. Tā kā jums jāgaida, kamēr abi atzīme un slaucīt fāzes ir pabeigtas, lai atgūtu atmiņu, šis algoritms rada pārtraukuma laika problēmas lielākām kaudzēm un lielākām reālām datu kopām.

Viens no veidiem, kā jūs varat palīdzēt ļoti atmiņu patērējošām lietojumprogrammām, ir izmantot GC regulēšanas opcijas, kas atbilst dažādiem lietojumu scenārijiem un vajadzībām. Pielāgošana daudzos gadījumos var palīdzēt vismaz atlikt kādu no šiem posmiem, lai tie nekļūtu par risku jūsu lietojumprogrammai vai pakalpojumu līmeņa līgumiem (SLA). (SLA norāda, ka lietojumprogramma atbildīs noteiktiem lietojuma reakcijas laikiem, t.i., latentumam.) Katras slodzes maiņas un lietojumprogrammas modifikācijas iestatīšana ir atkārtots uzdevums, taču tā kā noregulēšana ir derīga tikai noteiktai darba slodzei un piešķiršanas ātrumam.

Iezīmēšanas un slaucīšanas ieviešana

Ir vismaz divas komerciāli pieejamas un pārbaudītas pieejas marķējumu un slaucīšanas kolekcijas ieviešanai. Viena ir paralēlā pieeja, bet otra - vienlaicīgā (vai galvenokārt vienlaicīgā) pieeja.

Paralēli kolekcionāri

Paralēla kolekcija nozīmē, ka procesam piešķirtie resursi tiek izmantoti paralēli atkritumu savākšanai. Lielākā daļa komerciāli realizēto paralēlo savācēju ir monolīti kolektori visā pasaulē - visi lietojuma pavedieni tiek apturēti, līdz viss atkritumu savākšanas cikls ir pabeigts. Visu pavedienu apturēšana ļauj vienlaikus efektīvi izmantot visus resursus, lai pabeigtu atkritumu savākšanu, izmantojot atzīmi un slaucīšanas fāzes. Tas noved pie ļoti augsta efektivitātes līmeņa, kas parasti rada augstus rādītājus tādos caurlaidspējas kritērijos kā SPECjbb. Ja jūsu lietojumam ir būtiska caurlaidspēja, lieliska izvēle ir paralēla pieeja.

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