Un finalizator , în limbajele de programare orientate pe obiecte care utilizează colectarea de gunoi , este o metodă specială numită de runtime înainte ca un obiect să fie eliminat de către colectorul de gunoi.
Un finalizator este o metodă de clasă care este apelată automat de runtime între momentul în care un obiect din acea clasă este recunoscut ca neutilizat de către colectorul de gunoi și momentul în care obiectul este îndepărtat (memoria pe care o ocupă este eliberată). Un finalizator pentru un anumit obiect este întotdeauna executat după ce programul încetează să folosească obiectul și înainte ca memoria obiectului să fie eliberată de către colectorul de gunoi. Este convenabil să ne gândim la finalizator ca fiind apelat chiar înainte ca obiectul să fie eliminat din memorie, deși acest lucru nu este de obicei garantat.
La suprafață, un finalizator este similar cu un destructor de clasă , dar, în realitate, efectul și domeniul de aplicare al acestor metode sunt destul de diferite. Diferența se datorează faptului că momentul în care este apelat finalizatorul, spre deosebire de destructor, nu este definit rigid: finalizatorul este apelat întotdeauna înainte ca obiectul să fie distrus de către colectorul de gunoi, dar momentul distrugerii depinde de modul de funcționarea colectorului de gunoi, cantitatea de RAM disponibilă și activitatea de utilizare a memoriei a programului. Deci, dacă există puțină memorie liberă și crearea de noi obiecte are loc în mod constant, nevoia de colectare a gunoiului apare frecvent și, în consecință, finalizatorul este foarte probabil să fie apelat imediat după ce obiectul nu mai este utilizat. Dacă există multă memorie, iar consumul acesteia de către program este mic, atunci poate dura mult timp de la încetarea utilizării obiectului până la colectarea gunoiului (și apelul finalizatorului). Mai mult decât atât, dacă există multă memorie și sunt create puține sau deloc obiecte noi, atunci colectorul de gunoi poate să nu fie apelat deloc, iar la sfârșitul programului, toată memoria alocată acestuia va fi pur și simplu returnată la sistemul de operare. sistem; în acest caz, finalizatorul poate să nu fie apelat deloc.
În timp ce destructorii sunt foarte des folosiți pentru a elibera resursele de sistem limitate (cum ar fi accesul la un fișier sau hardware ) ocupate de un obiect, finalizatoarele, datorită caracteristicilor menționate mai sus, nu sunt de obicei recomandate să fie utilizate în acest mod. Desigur, finalizatorul poate închide fișierul sau poate spune sistemului de operare că dispozitivul nu mai este în uz, totuși, poate trece o perioadă nedeterminată de timp din momentul în care obiectul nu mai este folosit până la momentul în care este apelat finalizatorul și în tot acest timp resursele ocupate de obiect nu vor fi folosite, ci vor rămâne ocupate. [unu]
Finalizatoarele sunt imprevizibile, adesea periculoase și adesea inutile.
— Joshua Bloch. Java eficient. Addison-Westley, 2001.Ca urmare a celor de mai sus, utilizarea finalizatoarelor este foarte limitată. Limbile colectate de gunoi folosesc modelul de proiectare „elimină” pentru a dealoca resurse . Limbajul de programare C# acceptă implicit modelul „dispose” prin interfața IDisposable și cuvântul cheie using, iar Java 7 a introdus un mecanism similar „try-with-resources”.
Unul dintre rarele cazuri în care un finalizator este cu adevărat necesar este atunci când o clasă implementează propriile mecanisme de gestionare a memoriei care se bazează pe codul terță parte care nu este gestionat de sistemul de colectare a gunoiului, de exemplu, când o clasă Java folosește cod scris în C pentru a realiza eficiență maximă sau efectuați operațiuni de nivel scăzut. Pentru ca codul extern să funcționeze, memoria trebuie alocată folosind mecanisme standard C (malloc) și eliberată cu ajutorul propriu (gratuit). Puteți (și de obicei ar trebui) să apelați funcția de alocare a memoriei în constructorul de clasă, iar locul corect pentru a apela funcția de dealocare a memoriei externe este chiar în finalizator, deoarece această locație asigură că memoria pentru codul extern este alocată înaintea obiectului. este folosit (când a fost creat) și eliberat numai când încetează utilizarea sa. Dacă finalizatorul nu este apelat imediat sau chiar nu este apelat deloc, nu se va întâmpla nimic rău, deoarece memoria externă alocată va fi în continuare returnată automat în sistem după terminarea programului.
O altă modalitate bună de a folosi un finalizator este să vă asigurați că un obiect este curățat înainte de a fi șters. Dacă un obiect captează resurse valoroase de sistem, altele decât memoria, în timpul creării sau în timpul funcționării sale (deschide fișiere sau canale de comunicare, se conectează la dispozitive I/O), atunci, evident, în momentul în care obiectul este șters de către colectorul de gunoi, toate aceste resurse ar trebui deja eliberate (atunci este un obiect de curățat). Erorile de curățare (când un obiect în unele situații nu este curățat sau, și mai rău, nu este curățat complet) sunt foarte insidioase, sunt greu de detectat, deoarece apar la executarea unei părți complet diferite a codului în care a fost eroarea. făcut. După cum sa menționat deja, nu este înțelept să curățați în finalizator, deoarece nu se știe când va fi apelat și dacă va fi apelat deloc. Dar în finalizator, este destul de potrivit și convenabil să verificați dacă obiectul este complet șters și să emiteți, într-o formă sau alta, un mesaj de eroare dacă o resursă rămâne capturată. Nu contează că finalizatorul poate fi apelat târziu și nu de fiecare dată; oricum, dacă există o eroare de curățare a obiectului, atunci mai devreme sau mai târziu finalizatorul o va „prinde”.
Finalizatoarele pot fi create în diferite moduri. În unele limbi, finalizatorul face parte din biblioteca standard. De obicei, în astfel de cazuri, este o metodă virtuală a clasei rădăcină standard, din care toate celelalte clase din sistem sunt descendente (în Java, aceasta este metoda finalize () a clasei Object). Finalizatoarele pot fi, de asemenea, declarate folosind o sintaxă specială. În C# , sintaxa pentru declararea unui finalizator este împrumutată de la destructorii C++ - finalizatorul pentru clasa Class devine metoda cu semnătura ~Class(). Limbajul Nemerle , care se bazează pe C#, a abandonat această sintaxă deoarece era considerată predispusă la erori.