Oto jak rozumiem kod źródłowy the documentation i . Ja też może być całkowicie nie tak, ponieważ nie jestem programistą V8, a dokumentacja jest skąpa.
Śmieciarz będzie niekiedy przenosił rzeczy z jednego miejsca pamięci do drugiego, a podczas jednego takiego przeciągnięcia sprawdza również, które obiekty są nadal dostępne, a które nie. W przeciwieństwie do typów zliczających odniesienia, takich jak std::shared_ptr
, jest w stanie wykryć i zebrać cykliczne struktury danych. Aby wszystko to działało, V8 musi dobrze wiedzieć, jakie obiekty są osiągalne.
Z drugiej strony obiekty są tworzone i usuwane całkiem sporo podczas wewnętrznych obliczeń niektórych obliczeń. Nie potrzebujesz zbyt dużego narzutu dla każdej takiej operacji. Aby to osiągnąć, należy utworzyć stos uchwytów. Każdy obiekt wymieniony w tym stosie jest dostępny od jakiegoś uchwytu w niektórych obliczeniach C++. Oprócz tego istnieją trwałe porty, które prawdopodobnie wymagają więcej pracy do skonfigurowania i które mogą przetrwać poza obliczeniami C++.
Posiadanie stosu odnośników wymaga użycia tego w sposób podobny do stosu. W tym stosie nie ma "nieważnego" znaku. Wszystkie obiekty od dołu do góry stosu są poprawnymi odniesieniami do obiektów. Sposób zapewnienia tego jest LocalScope
. Utrzymuje rzeczy w hierarchii. W odniesieniu liczone wskaźniki można zrobić coś takiego:
shared_ptr<Object>* f() {
shared_ptr<Object> a(new Object(1));
shared_ptr<Object>* b = new shared_ptr<Object>(new Object(2));
return b;
}
void g() {
shared_ptr<Object> c = *f();
}
Tutaj przedmiot 1 jest tworzony pierwszy następnie przedmiot 2 jest tworzony, a następnie powraca funkcyjne i przedmiot 1 jest zniszczony, to obiekt 2 jest zniszczona . Kluczową kwestią jest to, że istnieje moment, w którym obiekt 1 jest nieważny, ale obiekt 2 jest nadal ważny. To właśnie chce uniknąć LocalScope
.
Niektóre inne implementacje GC badają stos C i szukają znalezionych w nich wskaźników. Ma to dużą szansę na fałszywe alarmy, ponieważ rzeczy, które w rzeczywistości są danymi, mogą zostać błędnie zinterpretowane jako wskaźnik. Dla osiągnięcia tego może wydawać się nieszkodliwe, ale przy przepisywaniu wskaźników, ponieważ poruszasz obiektami, może to być śmiertelne. Ma wiele innych wad i wiele zależy od tego, jak działa niski poziom implementacji języka. V8 unika tego poprzez utrzymywanie stosu uchwytów oddzielnie od stosu wywoływania funkcji, przy jednoczesnym zapewnieniu, że są one wystarczająco wyrównane, aby zagwarantować wspomniane wymagania hierarchii.
Aby zaoferować jeszcze jedno porównanie: odniesienia do obiektów przez jeden shared_ptr
stają się kolekcjonerskie (i tak naprawdę zostaną zebrane) po zakończeniu zakresu bloku C++. Obiekt oznaczony jako v8::Handle
stanie się kolekcjonerski po opuszczeniu najbliższego otaczającego zakresu, który zawierał obiekt HandleScope
. Tak więc programiści mają większą kontrolę nad ziarnistością operacji na stosie.W ciasnej pętli, w której wydajność jest ważna, może być użyteczne utrzymywanie pojedynczego HandleScope
dla całego obliczenia, więc nie będziesz musiał tak często uzyskiwać dostępu do struktury danych stosu uchwytów. Z drugiej strony, spowoduje to zatrzymanie wszystkich obiektów przez cały czas obliczeń, co byłoby bardzo złe, gdyby to była pętla powtarzająca się w wielu wartościach, ponieważ wszystkie z nich byłyby przechowywane do końca. Ale programista ma pełną kontrolę i może zorganizować wszystko w najbardziej odpowiedni sposób.
Osobiście bym upewnić skonstruować HandleScope
- Na początku każdej funkcji, które mogą być wywoływane z zewnątrz kodzie. Zapewni to, że twój kod oczyści się po nim.
- W ciele każdej pętli, która może widzieć więcej niż trzy lub więcej iteracji, tak aby zachować tylko zmienne z bieżącej iteracji.
- Wokół każdego bloku kodu, po którym następuje wywołanie wywołania zwrotnego, ponieważ gwarantuje to, że twoje rzeczy mogą zostać wyczyszczone, jeśli wywołanie zwrotne wymaga więcej pamięci.
- Ilekroć wydaje mi się, że coś może wytwarzać znaczne ilości danych pośrednich, które powinny zostać oczyszczone (lub przynajmniej stać się kolekcjonerskimi) tak szybko, jak to możliwe.
W ogóle nie chciałbym tworzyć HandleScope
dla każdej funkcji wewnętrznej, jeśli mogę mieć pewność, że każda inna funkcja nazywając to będzie już utworzyły HandleScope
. Ale to prawdopodobnie kwestia gustu.
Czy każda karta w chrome nie działa w oddzielnym procesie? – Pete
@Pete HandleScope może być (opcjonalnie) uruchomiony w swoich własnych wątkach (również w procesie). Dodatkowo, ponieważ każda z nich jest ich własną zmienną przestrzenią, nie ma potrzeby zapewnienia, że ich zmienne są "bezpieczne dla wątków" między sobą. – PicoCreator