Circular reference in JavaScript November 24, 2009 14:13 over 2 years ago
En cirkulær reference er en bidirektionel forbindelse mellem to objekter hvor hver instans har en reference til det andet objekt. I et “garbage collected” system er dette ikke noget problem. Begge objekter vil blive fjernet samtidigt når de ikke længere bliver brugt. Ved traversering af stakken holder processen selv øje med objekter som kun bliver refereret fra andre objekter. Hvis systemet er baseret på en tællefunktion pr referencer kan ingen af disse objekter blive elimineret fordi referencer tælleren aldrig kommer tilbage på 0. I et hybrid system hvor man både benytter optælling og “garbage collection” vil der opstå en leak fordi der ikke kan erkendes en cirkulær reference ud fra stakken.
Der finde mange forskellige implementationer af gode og mindre gode javascript motere. Man kan se en oversigt på Timeline of web browsers
Man kan fx lege lidt med den cirkulære reference på forskellige browser versioner for at se hvor store forskelle der faktisk findes. Det er ikke Javascript som programmeringssprog der er dårligt, men kun de Javascript fortolkere der benyttes. Desværre er det ofte op til den enkelte programmør at kende til de mange revisioner. Dette issue er vel i virkeligheden det svageste punkt ved Javascript.
Dette er grunden til at de fleste vil bruge et Javascript bibliotek som er Cross Browser Enable robust. Disse bibliotker er naturligvis abstraktioner og når man arbejder i en enterprisey virksomhed kommer der et tidspunkt hvor man må lave en helt specielt løsning. Uagtet om ideen er god eller dårlig. Derfor udvikler du selv to linjer Javascript og indsætter dem som en HOTFIX og merger dem direkte ind i produktion. Ups, vi har alle været der.
Her er et par eksempler på hvordan man kan ødelægge enhver browsers javascript motor.
- Start din task manager så du kan se hvad din computer fortager sig!
- Start din alletiders favorit browser!
- Tryk på knapperne så hurtigt du kan!
- Hvis du bruger en rigtig browser kan du følge CPU usage og Pagefile usage!
- Prøv gerne forskellige typer og versioner af browserer.
Jeg håber din produktionskode er mere kompleks og det vil være langt nemmere at lave disse operationer ved et uheld. Det er let at lave bugs når man ikke ved hvordan man laver bugs. Fixing bugs doesn’t make the bugs go away!
Eksemplet viser hvordan en cirkulær reference dannes mellem DOM objektet og Javascript objektet.
// Circular references between JavaScript and DOM
function circular_references_between_javaScript_and_dom() {
var object;
object=document.getElementById("element");
document.getElementById("element").expandoProperty=object;
object.bigString=new Array(2000).join(new Array(2000).join("XXXXX"));
}
<div id="element">
<input type="button" value="circular_references" onclick="circular_references()">
I dette tilfælde vil mange gode browsere kunne håndtere denne function mens andre vil løbe tør for memory med det samme. Den væsentligste grund er den valgte memory model i browserens javascript motor. Hvis den er ren “garbage collected” som fx firefox 3 kan den nemt klare beslastningen. Måske vil den hænge lidt men ikke mere. IE4+ til IE6 har implementeret en hybrid memory model og kan derfor ikke gennemskue bidirektionelle (cirkulære) referencer. Jeg har ikke prøvet med Google Chrome’s V8 javascript motor men efter som det er Lars Bak der har bygget den er jeg sikker på at den nemt kan klare den slags “hotspots”.
Hvis man har interesse både i virtuelle maskiner og Javascript har man nok bemærket at teamet i Århus er bemandet med en stor del af del mennesker fra det oprindelige hotspot team. JIT principperne sprang ud af Strongtalk, Smalltalk og programmeringssproget Self Self er måske et af de vanskeligste programmeringssprog at skabe god bytecode for pga de dynamiske egenskaber. V8 motoren bruger mange af de samme teknikker som Java Hotspot for at få Javascript til at køre hurtigt. Fx bruger V8 hidden classes til et forbedre “Fast Property Access” som er dynamisk addition af properties on runtime og derved øges kontrollen over opslaget af den valgte property betydeligt. Når Google Chrome OS også kommer på banen bliver alle disse optimeringer endnu bedre hvilket giver os brugere bedre performance og mindre at tænke på.
Closures and circular references
I den næste stump kode kaldes en closure fra Javascript der indeholder en reference til et DOM objekt. DOM elementet har til gengæld reference til Javasript objektet. Den resulterende cirkulære reference mellem javascript objekt og DOM objekt forårsager en memory leak.
Eksemplet viser hvordan man addere et javascript objekt (closure) til DOM objektet.
// Memory leak by closure
function memory_leak_by_closure() {
var parentDiv = document.createElement("div");
parentDiv.onclick=function(){
foo();
};
parentDiv.bigString = new Array(2000).join(new Array(2000).join("XXXXX"));
}
<input type="button" value="memory_leak_by_closure" onclick="memory_leak_by_closure()">
Closures er en af de mest kraftfulde funktioner i Javascript men det kan være svært at forstå dem. De er nemme at konstruere, selv ved et uheld. Deres eksistens er potentielt farlige for almindelige browsere og det er nødvendigt at forstår deres fulde omfang. Dette gælder ikke kun Javascript.
Grunden til at jeg sidder og tager enterprisey “Your Moment of Zen” er at vores produktionskode fejler bigtime i IE Explorer version 7- Det er værd at huske at version 7 næsten version 6 med forbedre GC og version 6 næste er version 4.5 med lidt hotfix’es. Nuvel, i Javascript koden er konstrueret en timer som har til mål at løbe ud af en tidsramme inden nogle andre systemer timer ud, for at forhindre en “ufin” lukning for brugeren.
Måske kan man forstille sig de vilkår vi som softwareudviklere arbejder under i kampen på at forbedrede interaktionen med brugerne.
By Frank Vilhelmsen - 1 tag: javascript - Add comment