En finalizer , i objektorienterade programmeringsspråk som använder sophämtning , är en speciell metod som anropas av körtiden innan ett objekt kasseras av sopsamlaren.
En finalizer är en klassmetod som automatiskt anropas av körtiden mellan det att ett objekt i den klassen identifieras som oanvänt av sopsamlaren och den tid då objektet tas bort (minnet som det upptar frigörs). En finalizer för ett visst objekt exekveras alltid efter att programmet slutat använda objektet och innan objektets minne frigörs av sopsamlaren. Det är bekvämt att tänka på att finalizern anropas precis innan objektet tas bort från minnet, även om detta vanligtvis inte är garanterat.
På ytan liknar en finalizer en klassförstörare , men i verkligheten är effekten och omfattningen av dessa metoder helt olika. Skillnaden beror på det faktum att ögonblicket när slutbehandlaren anropas, till skillnad från förstöraren, inte är strikt definierad: slutbehandlaren anropas alltid innan föremålet förstörs av sopsamlaren, men förstöringsögonblicket beror på läget för sopsamlarens funktion, mängden tillgängligt RAM-minne och programmets minnesanvändningsaktivitet. Så om det finns lite ledigt minne och skapandet av nya objekt sker ständigt, uppstår behovet av sophämtning ofta och slutföraren kommer därför med stor sannolikhet att anropas strax efter att objektet inte längre används. Om det finns mycket minne och konsumtionen av programmet är liten, kan det ta lång tid från att objektet upphör att använda till sophämtning (och anropet från slutföraren). Dessutom, om det finns mycket minne och få eller inga nya objekt skapas, kan sopsamlaren inte anropas alls, och i slutet av programmet kommer allt minne som tilldelats den helt enkelt att återföras till driften. systemet; i det här fallet kanske slutföraren inte anropas alls.
Medan destruktörer mycket ofta används för att frigöra knappa systemresurser (såsom åtkomst till en fil eller hårdvara ) som är upptagna av ett objekt, rekommenderas slutbehandlare, på grund av funktionerna ovan, vanligtvis inte att användas på detta sätt. Naturligtvis kan slutbehandlaren stänga filen eller tala om för operativsystemet att enheten inte längre används, men det kan gå en obestämd tid från det att objektet inte längre används till det ögonblick slutbehandlaren anropas, och hela denna tid kommer resurserna som upptas av objektet inte att användas, men kommer att förbli upptagna. [ett]
Finalizers är oförutsägbara, ofta farliga och ofta onödiga.
— Joshua Bloch. Effektiv Java. Addison-Westley, 2001.Som ett resultat av ovanstående är användningen av slutbehandlare mycket begränsad. Skräpinsamlade språk använder designmönstret "kassera" för att deallokera resurser . Programmeringsspråket C# stöder "avyttra"-mönstret implicit genom gränssnittet IDisposable och nyckelordet using, och Java 7 introducerade en liknande "prova-med-resurser"-mekanism.
Ett av de sällsynta fallen där en finalizer verkligen är nödvändig är när en klass implementerar sina egna minneshanteringsmekanismer som förlitar sig på tredjepartskod som inte hanteras av sophämtningssystemet, till exempel när en Java-klass använder kod skriven i C för att uppnå maximal effektivitet eller utföra operationer på låg nivå. För att extern kod ska fungera måste minnet allokeras med standard C-mekanismer (malloc) och frigöras med egen hjälp (gratis). Du kan (och vanligtvis bör) anropa minnesallokeringsfunktionen i klasskonstruktorn, och den korrekta platsen att anropa den externa minnesavallokeringsfunktionen är precis i finalizern, eftersom denna plats säkerställer att minnet för den externa koden allokeras före objektet används (när den skapades) och släpps först när användningen upphör. Om finalizern inte anropas omedelbart eller till och med inte anropas alls, kommer inget dåligt att hända, eftersom det tilldelade externa minnet fortfarande kommer att återföras till systemet automatiskt efter programmets slut.
Ett annat bra sätt att använda en finalizer är att se till att ett objekt rensas upp innan det raderas. Om ett objekt fångar andra värdefulla systemresurser än minne under skapandet eller under dess drift (öppnar filer eller kommunikationskanaler, ansluter till I/O-enheter), så är det uppenbarligen i det ögonblick som objektet raderas av sopsamlaren, alla dessa resurser borde redan vara frigjorda (då finns det ett objekt som ska rensas). Rensningsfel (när ett objekt i vissa situationer inte rensas upp eller, ännu värre, inte rensas upp helt) är mycket lömska, de är svåra att upptäcka, eftersom de uppstår när en helt annan del av koden körs där felet uppstod. gjord. Som redan nämnts är det oklokt att städa upp i finalizern, eftersom det inte är känt när det kommer att anropas och om det överhuvudtaget kommer att anropas. Men i slutbehandlaren är det ganska lämpligt och bekvämt att kontrollera om objektet är helt rensat och utfärda, i en eller annan form, ett felmeddelande om någon resurs förblir fångad. Det spelar ingen roll att finalisten kan kallas sent och inte varje gång; Hur som helst, om det finns ett objektrensningsfel, kommer slutbehandlaren förr eller senare att "fånga" det.
Finalizers kan skapas på olika sätt. På vissa språk är finalizern en del av standardbiblioteket. Vanligtvis i sådana fall är det en virtuell metod av standardrotklassen, från vilken alla andra klasser i systemet är ättlingar (i Java är detta finaliseringsmetoden () för klassen Object). Finalizers kan också deklareras med hjälp av speciell syntax. I C# är syntaxen för att deklarera en finalizer lånad från C++ destructors - finalizern för klassen Class blir metoden med ~Class()-signaturen. Nemerle - språket , som är baserat på C#, övergav denna syntax eftersom det ansågs felbenäget.