2015-02-04 17 views
15

Spotkałem się z severalplaces, gdzie ludzie dzwonią pod numer collectgarbage()dwa razy pod, aby sfinalizować wszystkie nieużywane obiekty.Dlaczego musimy dwukrotnie wywoływać funkcję collectgarbage() Lua?

Dlaczego tak jest? Dlaczego nie wystarczy jedno połączenie? Dlaczego nie trzy połączenia?

Kiedy próbuję następujący kod (na Lua 5.2), obiekt zostanie ukończony (co znaczy: jego __gc pobiera nazywa) tylko z jednego połączenia do collectgarbage:

do 
    local x = setmetatable({},{ 
    __gc = function() print("works") end 
    }) 
end 
collectgarbage() 
os.exit() 

Czy to oznacza jedno połączenie jest wystarczająco ?

Odpowiedz

14

Wyjaśniono w Programowanie w Lua Wydanie trzecie §17.6 Finalizery. Krótko mówiąc, to z powodu zmartwychwstania.

Finalizator jest funkcją związaną z obiektem, który jest wywoływany, gdy obiekt ma zostać zebrany. Lua implementuje finalizatory z metametodem __gc.

Problem polega na tym, że po wywołaniu finalizatorów obiekt musi być w niektórych przypadkach żywy. pil wyjaśnia z tego przykładu:

A = {x = "this is A"} 
B = {f = A} 
setmetatable(B, {__gc = function (o) print(o.f.x) end}) 
A, B = nil 
collectgarbage() --> this is A 

Finalizer dla B dostęp A, więc A nie mogą być odebrane przed zakończeniem B. Lua musi wskrzesić zarówno B i A przed uruchomieniem tego finalizatora.

Zmartwychwstanie jest powód wywoływania collectgarbage dwukrotnie:

Ponieważ zmartwychwstania obiektów z finalizatory zbiera się w dwóch etapach. Po raz pierwszy kolektor wykryje, że obiekt z finalizatorem nie jest dostępny, kolektor wskrzesi obiekt i kolejkuje go do sfinalizowania. Po uruchomieniu finalizatora Lua zaznacza obiekt jako sfinalizowany. Następnym razem, gdy kolektor wykryje, że obiekt nie jest osiągalny, usuwa obiekt. Jeśli chcesz upewnić się, że wszystkie śmieci w twoim programie zostały faktycznie zwolnione, musisz dwukrotnie zadzwonić pod numer collectgarbage; drugie połączenie usunie obiekty, które zostały sfinalizowane podczas pierwszego połączenia.

+0

Cytat "drugie połączenie spowoduje skasowanie obiektów, które zostały sfinalizowane podczas pierwszego połączenia" (i "Następnym razem, gdy kolektor wykryje [...] usunie obiekt") mówi, jeśli rozumiem go poprawnie , że finalizator ('__gc') jest uruchamiany na [końcu **] pierwszego połączenia do' collectgarbage() '. Następnie ** drugie ** wywołanie 'coollectgarbage' po prostu zwalnia pamięć (manual:" pamięć obiektu jest zwalniana w następnym cyklu zbierania śmieci ". PiL: zobacz poprzedni cytat). Mój własny przykład (kod w moim pytaniu) rzekomo "dowodzi" mojego rozumienia. Ale ... –

+0

... Ale moje zrozumienie musi być złe, ponieważ mój [drugi link] (http://sourceforge.net/p/luabind/mailman/message/23606367/) dowodzi, że jest źle (tj. Finalizator nie był " t zadzwonił na pierwsze 'collectgarbage', zobacz" Musiałem dodać jeszcze jedno wywołanie collectgarbage() ") i [pierwszy link] (http://stackoverflow.com/questions/19488814/does-luajit-support-gc-for -tables) zawiera komentarz (przez użytkownika), który mówi: "[calling] dwa razy zapewnia wywołanie wszystkich finalizatorów" (może ten komentarz jest nieprawidłowy). Więc wciąż jestem w stracie. –

+0

Ooops, widzę mój błąd. Mój [pierwszy link] (http: // sourceforge.net/p/luabind/listonosz/wiadomość/23606367 /) zajmuje się Lua 5.1.4, która nie ma finalizatory, więc jest to zupełnie bez znaczenia dla nas. Przepraszam. –

Powiązane problemy