Apgūstiet JavaScript atmiņas pārvaldību un atkritumu savākšanu. Uzziniet optimizācijas metodes, lai uzlabotu lietojumprogrammu veiktspēju un novērstu atmiņas noplūdes.
JavaScript atmiņas pārvaldība: atkritumu savākšanas optimizācija
JavaScript, kas ir mūsdienu tīmekļa izstrādes stūrakmens, lielā mērā paļaujas uz efektīvu atmiņas pārvaldību, lai nodrošinātu optimālu veiktspēju. Atšķirībā no tādām valodām kā C vai C++, kur izstrādātājiem ir manuāla kontrole pār atmiņas iedalīšanu un atbrīvošanu, JavaScript izmanto automātisku atkritumu savākšanu (GC). Lai gan tas vienkāršo izstrādi, izpratne par to, kā GC darbojas un kā optimizēt savu kodu, ir būtiska, lai veidotu atsaucīgas un mērogojamas lietojumprogrammas. Šis raksts iedziļinās JavaScript atmiņas pārvaldības sarežģītībās, koncentrējoties uz atkritumu savākšanu un optimizācijas stratēģijām.
Izpratne par atmiņas pārvaldību JavaScript
JavaScript valodā atmiņas pārvaldība ir process, kurā tiek iedalīta un atbrīvota atmiņa datu glabāšanai un koda izpildei. JavaScript dzinējs (piemēram, V8 pārlūkā Chrome un Node.js, SpiderMonkey pārlūkā Firefox vai JavaScriptCore pārlūkā Safari) automātiski pārvalda atmiņu aizkulisēs. Šis process ietver divus galvenos posmus:
- Atmiņas iedalīšana: Atmiņas vietas rezervēšana mainīgajiem, objektiem, funkcijām un citām datu struktūrām.
- Atmiņas atbrīvošana (atkritumu savākšana): Atmiņas atgūšana, kuru lietojumprogramma vairs neizmanto.
Atmiņas pārvaldības galvenais mērķis ir nodrošināt, ka atmiņa tiek izmantota efektīvi, novēršot atmiņas noplūdes (kad neizmantota atmiņa netiek atbrīvota) un samazinot ar iedalīšanu un atbrīvošanu saistītās pieskaitāmās izmaksas.
JavaScript atmiņas dzīves cikls
Atmiņas dzīves ciklu JavaScript var apkopot šādi:
- Iedalīt: JavaScript dzinējs iedala atmiņu, kad jūs izveidojat mainīgos, objektus vai funkcijas.
- Izmantot: Jūsu lietojumprogramma izmanto iedalīto atmiņu datu lasīšanai un rakstīšanai.
- Atbrīvot: JavaScript dzinējs automātiski atbrīvo atmiņu, kad tas nosaka, ka tā vairs nav nepieciešama. Šajā brīdī tiek veikta atkritumu savākšana.
Atkritumu savākšana: kā tā darbojas
Atkritumu savākšana ir automātisks process, kas identificē un atgūst atmiņu, ko aizņem objekti, kuri vairs nav sasniedzami vai kurus lietojumprogramma vairs neizmanto. JavaScript dzinēji parasti izmanto dažādus atkritumu savākšanas algoritmus, tostarp:
- Iezīmēšanas un tīrīšanas (Mark and Sweep): Šis ir visizplatītākais atkritumu savākšanas algoritms. Tas ietver divas fāzes:
- Iezīmēšana: Atkritumu savācējs šķērso objektu grafu, sākot no saknes objektiem (piemēram, globālajiem mainīgajiem), un iezīmē visus sasniedzamos objektus kā "dzīvus".
- Tīrīšana: Atkritumu savācējs pārskata kaudzi (atmiņas apgabalu, ko izmanto dinamiskai iedalīšanai), identificē neiezīmētos objektus (tos, kas nav sasniedzami) un atgūst to aizņemto atmiņu.
- Atsauču skaitīšana: Šis algoritms seko līdzi katra objekta atsauču skaitam. Kad objekta atsauču skaits sasniedz nulli, tas nozīmē, ka uz objektu vairs neatsaucas neviena cita lietojumprogrammas daļa, un tā atmiņu var atgūt. Lai gan to ir vienkārši ieviest, atsauču skaitīšanai ir būtisks trūkums: tā nevar atklāt cikliskas atsauces (kur objekti atsaucas viens uz otru, radot ciklu, kas neļauj to atsauču skaitam sasniegt nulli).
- Paaudžu atkritumu savākšana: Šī pieeja sadala kaudzi "paaudzēs", pamatojoties uz objektu vecumu. Ideja ir tāda, ka jaunāki objekti, visticamāk, kļūs par atkritumiem nekā vecāki objekti. Atkritumu savācējs biežāk koncentrējas uz "jaunās paaudzes" savākšanu, kas parasti ir efektīvāk. Vecākās paaudzes tiek savāktas retāk. Tas balstās uz "paaudžu hipotēzi".
Mūsdienu JavaScript dzinēji bieži apvieno vairākus atkritumu savākšanas algoritmus, lai panāktu labāku veiktspēju un efektivitāti.
Atkritumu savākšanas piemērs
Apskatīsim šādu JavaScript kodu:
function createObject() {
let obj = { name: "Example", value: 123 };
return obj;
}
let myObject = createObject();
myObject = null; // Remove the reference to the object
Šajā piemērā funkcija createObject
izveido objektu un piešķir to mainīgajam myObject
. Kad myObject
tiek iestatīts uz null
, atsauce uz objektu tiek noņemta. Atkritumu savācējs galu galā identificēs, ka objekts vairs nav sasniedzams, un atgūs tā aizņemto atmiņu.
Biežākie atmiņas noplūžu cēloņi JavaScript
Atmiņas noplūdes var ievērojami pasliktināt lietojumprogrammas veiktspēju un izraisīt avārijas. Izpratne par biežākajiem atmiņas noplūžu cēloņiem ir būtiska to novēršanai.
- Globālie mainīgie: Nejauša globālo mainīgo izveide (izlaižot atslēgvārdus
var
,let
vaiconst
) var izraisīt atmiņas noplūdes. Globālie mainīgie pastāv visā lietojumprogrammas dzīves cikla laikā, neļaujot atkritumu savācējam atgūt to atmiņu. Vienmēr deklarējiet mainīgos, izmantojotlet
vaiconst
(vaivar
, ja nepieciešama funkcijas tvēruma darbība) atbilstošā tvērumā. - Aizmirsti taimeri un atzvanīšanas funkcijas:
setInterval
vaisetTimeout
izmantošana, tos pienācīgi nenotīrot, var izraisīt atmiņas noplūdes. Ar šiem taimeriem saistītās atzvanīšanas funkcijas var uzturēt objektus dzīvus pat pēc tam, kad tie vairs nav nepieciešami. IzmantojietclearInterval
unclearTimeout
, lai noņemtu taimerus, kad tie vairs nav vajadzīgi. - Noslēgumi (Closures): Noslēgumi dažkārt var izraisīt atmiņas noplūdes, ja tie netīši uztver atsauces uz lieliem objektiem. Esiet uzmanīgi ar mainīgajiem, kurus uztver noslēgumi, un nodrošiniet, ka tie nevajadzīgi neaiztur atmiņu.
- DOM elementi: Atsauču turēšana uz DOM elementiem JavaScript kodā var novērst to atkritumu savākšanu, īpaši, ja šie elementi tiek noņemti no DOM. Tas ir biežāk sastopams vecākās Internet Explorer versijās.
- Cikliskās atsauces: Kā minēts iepriekš, cikliskas atsauces starp objektiem var liegt atsauču skaitīšanas atkritumu savācējiem atgūt atmiņu. Lai gan mūsdienu atkritumu savācēji (piemēram, Mark and Sweep) parasti spēj tikt galā ar cikliskām atsaucēm, joprojām ir laba prakse no tām izvairīties, ja iespējams.
- Notikumu klausītāji: Aizmirstot noņemt notikumu klausītājus no DOM elementiem, kad tie vairs nav nepieciešami, arī var rasties atmiņas noplūdes. Notikumu klausītāji uztur saistītos objektus dzīvus. Izmantojiet
removeEventListener
, lai atvienotu notikumu klausītājus. Tas ir īpaši svarīgi, strādājot ar dinamiski izveidotiem vai noņemtiem DOM elementiem.
JavaScript atkritumu savākšanas optimizācijas metodes
Lai gan atkritumu savācējs automatizē atmiņas pārvaldību, izstrādātāji var izmantot vairākas metodes, lai optimizētu tā veiktspēju un novērstu atmiņas noplūdes.
1. Izvairieties no nevajadzīgu objektu veidošanas
Liela skaita pagaidu objektu izveide var noslogot atkritumu savācēju. Atkārtoti izmantojiet objektus, kad vien iespējams, lai samazinātu iedalīšanas un atbrīvošanas skaitu.
Piemērs: Tā vietā, lai katrā cikla iterācijā izveidotu jaunu objektu, atkārtoti izmantojiet esošu objektu.
// Neefektīvi: katrā iterācijā izveido jaunu objektu
for (let i = 0; i < 1000; i++) {
let obj = { index: i };
// ...
}
// Efektīvi: atkārtoti izmanto to pašu objektu
let obj = {};
for (let i = 0; i < 1000; i++) {
obj.index = i;
// ...
}
2. Minimizējiet globālos mainīgos
Kā minēts iepriekš, globālie mainīgie pastāv visā lietojumprogrammas dzīves cikla laikā un nekad netiek savākti kā atkritumi. Izvairieties no globālo mainīgo veidošanas un tā vietā izmantojiet lokālos mainīgos.
// Slikti: izveido globālu mainīgo
myGlobalVariable = "Hello";
// Labi: izmanto lokālu mainīgo funkcijas ietvaros
function myFunction() {
let myLocalVariable = "Hello";
// ...
}
3. Notīriet taimerus un atzvanīšanas funkcijas
Vienmēr notīriet taimerus un atzvanīšanas funkcijas, kad tie vairs nav nepieciešami, lai novērstu atmiņas noplūdes.
let timerId = setInterval(function() {
// ...
}, 1000);
// Notīriet taimeri, kad tas vairs nav nepieciešams
clearInterval(timerId);
let timeoutId = setTimeout(function() {
// ...
}, 5000);
// Notīriet taimauta taimeri, kad tas vairs nav nepieciešams
clearTimeout(timeoutId);
4. Noņemiet notikumu klausītājus
Atvienojiet notikumu klausītājus no DOM elementiem, kad tie vairs nav nepieciešami. Tas ir īpaši svarīgi, strādājot ar dinamiski izveidotiem vai noņemtiem elementiem.
let element = document.getElementById("myElement");
function handleClick() {
// ...
}
element.addEventListener("click", handleClick);
// Noņemiet notikumu klausītāju, kad tas vairs nav nepieciešams
element.removeEventListener("click", handleClick);
5. Izvairieties no cikliskām atsaucēm
Lai gan mūsdienu atkritumu savācēji parasti spēj tikt galā ar cikliskām atsaucēm, joprojām ir laba prakse no tām izvairīties, ja iespējams. Pārtrauciet cikliskās atsauces, iestatot vienu vai vairākas atsauces uz null
, kad objekti vairs nav nepieciešami.
let obj1 = {};
let obj2 = {};
obj1.reference = obj2;
obj2.reference = obj1; // Cikliskā atsauce
// Pārtrauciet ciklisko atsauci
obj1.reference = null;
obj2.reference = null;
6. Izmantojiet WeakMap un WeakSet
WeakMap
un WeakSet
ir īpaši kolekciju veidi, kas neliedz to atslēgām (WeakMap
gadījumā) vai vērtībām (WeakSet
gadījumā) tikt savāktām kā atkritumiem. Tie ir noderīgi, lai saistītu datus ar objektiem, neliedzot atkritumu savācējam šos objektus atgūt.
WeakMap piemērs:
let element = document.getElementById("myElement");
let data = new WeakMap();
data.set(element, { tooltip: "This is a tooltip" });
// Kad elements tiek noņemts no DOM, tas tiks savākts kā atkritumi,
// un arī saistītie dati WeakMap tiks noņemti.
WeakSet piemērs:
let element = document.getElementById("myElement");
let trackedElements = new WeakSet();
trackedElements.add(element);
// Kad elements tiek noņemts no DOM, tas tiks savākts kā atkritumi,
// un tas tiks noņemts arī no WeakSet.
7. Optimizējiet datu struktūras
Izvēlieties atbilstošas datu struktūras savām vajadzībām. Neefektīvu datu struktūru izmantošana var radīt nevajadzīgu atmiņas patēriņu un lēnāku veiktspēju.
Piemēram, ja jums bieži jāpārbauda elementa esamība kolekcijā, izmantojiet Set
, nevis Array
. Set
nodrošina ātrāku uzmeklēšanas laiku (vidēji O(1)) salīdzinājumā ar Array
(O(n)).
8. Debouncing un Throttling
Debouncing un throttling ir metodes, ko izmanto, lai ierobežotu funkcijas izpildes biežumu. Tās ir īpaši noderīgas, apstrādājot notikumus, kas tiek aktivizēti bieži, piemēram, scroll
vai resize
notikumus. Ierobežojot izpildes biežumu, jūs varat samazināt darba apjomu, kas jāveic JavaScript dzinējam, kas var uzlabot veiktspēju un samazināt atmiņas patēriņu. Tas ir īpaši svarīgi mazjaudīgām ierīcēm vai vietnēm ar daudziem aktīviem DOM elementiem. Daudzas JavaScript bibliotēkas un ietvari nodrošina debouncing un throttling implementācijas. Vienkāršs throttling piemērs ir šāds:
function throttle(func, delay) {
let timeoutId;
let lastExecTime = 0;
return function(...args) {
const currentTime = Date.now();
const timeSinceLastExec = currentTime - lastExecTime;
if (!timeoutId) {
if (timeSinceLastExec >= delay) {
func.apply(this, args);
lastExecTime = currentTime;
} else {
timeoutId = setTimeout(() => {
func.apply(this, args);
lastExecTime = Date.now();
timeoutId = null;
}, delay - timeSinceLastExec);
}
}
};
}
function handleScroll() {
console.log("Scroll event");
}
const throttledHandleScroll = throttle(handleScroll, 250); // Izpildīt ne biežāk kā reizi 250ms
window.addEventListener("scroll", throttledHandleScroll);
9. Koda sadalīšana
Koda sadalīšana ir metode, kas ietver JavaScript koda sadalīšanu mazākos gabalos jeb moduļos, kurus var ielādēt pēc pieprasījuma. Tas var uzlabot jūsu lietojumprogrammas sākotnējo ielādes laiku un samazināt startēšanas brīdī izmantotās atmiņas apjomu. Mūsdienu saiņotāji, piemēram, Webpack, Parcel un Rollup, padara koda sadalīšanu salīdzinoši viegli īstenojamu. Ielādējot tikai to kodu, kas nepieciešams konkrētai funkcijai vai lapai, jūs varat samazināt kopējo lietojumprogrammas atmiņas nospiedumu un uzlabot veiktspēju. Tas palīdz lietotājiem, īpaši vietās ar zemu tīkla joslas platumu un ar mazjaudīgām ierīcēm.
10. Web Workers izmantošana skaitļošanas ietilpīgiem uzdevumiem
Web Workers ļauj palaist JavaScript kodu fona pavedienā, atsevišķi no galvenā pavediena, kas apstrādā lietotāja saskarni. Tas var novērst ilgstošu vai skaitļošanas ietilpīgu uzdevumu bloķēšanu galvenajā pavedienā, kas var uzlabot jūsu lietojumprogrammas atsaucību. Uzdevumu pārcelšana uz Web Workers var arī palīdzēt samazināt galvenā pavediena atmiņas nospiedumu. Tā kā Web Workers darbojas atsevišķā kontekstā, tie nedala atmiņu ar galveno pavedienu. Tas var palīdzēt novērst atmiņas noplūdes un uzlabot vispārējo atmiņas pārvaldību.
// main.js
const worker = new Worker('worker.js');
worker.postMessage({ task: 'heavyComputation', data: [1, 2, 3] });
worker.onmessage = function(event) {
console.log('Result from worker:', event.data);
};
// worker.js
self.onmessage = function(event) {
const { task, data } = event.data;
if (task === 'heavyComputation') {
const result = performHeavyComputation(data);
self.postMessage(result);
}
};
function performHeavyComputation(data) {
// Veic skaitļošanas ietilpīgu uzdevumu
return data.map(x => x * 2);
}
Atmiņas lietojuma profilēšana
Lai identificētu atmiņas noplūdes un optimizētu atmiņas lietojumu, ir būtiski profilēt jūsu lietojumprogrammas atmiņas lietojumu, izmantojot pārlūkprogrammas izstrādātāju rīkus.
Chrome DevTools
Chrome DevTools nodrošina jaudīgus rīkus atmiņas lietojuma profilēšanai. Lūk, kā tos izmantot:
- Atveriet Chrome DevTools (
Ctrl+Shift+I
vaiCmd+Option+I
). - Dodieties uz paneli "Memory".
- Atlasiet "Heap snapshot" vai "Allocation instrumentation on timeline".
- Uzņemiet kaudzes momentuzņēmumus dažādos jūsu lietojumprogrammas izpildes punktos.
- Salīdziniet momentuzņēmumus, lai identificētu atmiņas noplūdes un vietas, kur atmiņas lietojums ir augsts.
"Allocation instrumentation on timeline" ļauj reģistrēt atmiņas iedalīšanu laika gaitā, kas var būt noderīgi, lai identificētu, kad un kur rodas atmiņas noplūdes.
Firefox izstrādātāju rīki
Firefox izstrādātāju rīki arī nodrošina rīkus atmiņas lietojuma profilēšanai.
- Atveriet Firefox izstrādātāju rīkus (
Ctrl+Shift+I
vaiCmd+Option+I
). - Dodieties uz paneli "Performance".
- Sāciet ierakstīt veiktspējas profilu.
- Analizējiet atmiņas lietojuma grafiku, lai identificētu atmiņas noplūdes un vietas, kur atmiņas lietojums ir augsts.
Globāli apsvērumi
Izstrādājot JavaScript lietojumprogrammas globālai auditorijai, ņemiet vērā šādus ar atmiņas pārvaldību saistītus faktorus:
- Ierīču iespējas: Lietotājiem dažādos reģionos var būt ierīces ar atšķirīgām atmiņas iespējām. Optimizējiet savu lietojumprogrammu, lai tā efektīvi darbotos uz zemas klases ierīcēm.
- Tīkla apstākļi: Tīkla apstākļi var ietekmēt jūsu lietojumprogrammas veiktspēju. Minimizējiet datu apjomu, kas jāpārsūta pa tīklu, lai samazinātu atmiņas patēriņu.
- Lokalizācija: Lokalizēts saturs var prasīt vairāk atmiņas nekā nelokalizēts saturs. Esiet uzmanīgi ar savu lokalizēto resursu atmiņas nospiedumu.
Noslēgums
Efektīva atmiņas pārvaldība ir būtiska, lai veidotu atsaucīgas un mērogojamas JavaScript lietojumprogrammas. Izprotot, kā darbojas atkritumu savācējs, un izmantojot optimizācijas metodes, jūs varat novērst atmiņas noplūdes, uzlabot veiktspēju un radīt labāku lietotāja pieredzi. Regulāri profilējiet savas lietojumprogrammas atmiņas lietojumu, lai identificētu un risinātu potenciālās problēmas. Atcerieties ņemt vērā globālus faktorus, piemēram, ierīču iespējas un tīkla apstākļus, optimizējot savu lietojumprogrammu pasaules mēroga auditorijai. Tas ļauj JavaScript izstrādātājiem veidot veiktspējīgas un iekļaujošas lietojumprogrammas visā pasaulē.