La finalisation est utilisée pour nettoyer les ressources qui ne peuvent pas être libérées par le garbage collector. Par exemple, considérons un programme qui alloue (via certaines ressources native
API) directement à partir du système d'exploitation. Cela donne généralement une sorte de « poignée » (un descripteur de fichier UNIX ou Windows MANCHE, ou quelque chose de similaire):
class Wrapper {
private long handle;
private Handle(long h) {
handle = h;
}
private static native long getHandleFromOS();
static Wrapper allocate() {
return new Handle(getHandleFromOS());
}
}
Alors, ce qui se passe, si votre code attribue une instance de classe Wrapper
? Eh bien, la classe alloue une sorte de ressource spécifique au système d'exploitation et conserve une référence à celle-ci (le handle) dans une variable membre. Mais que se passe-t-il lorsque la dernière référence Java à une instance de wrapper est perdue? Maintenant, le garbage collector récupérera (à un moment donné) l'espace de l'instance de wrapper maintenant défunte. Mais qu'advient-il de la ressource du système d'exploitation allouée par le wrapper? Il sera divulgué dans le scénario ci-dessus, ce qui est une mauvaise chose, si c'est une ressource coûteuse, comme un descripteur de fichier. Pour permettre à votre code de nettoyer dans un tel scénario, il existe la méthode finalize
.
class Wrapper {
private long handle;
private Handle(long h) {
handle = h;
}
protected void finalize() {
returnHandleToOS(handle);
}
private static native long getHandleFromOS();
private static native void returnHandleToOS(long handle);
static Wrapper allocate() {
return new Handle(getHandleFromOS());
}
}
Maintenant, lorsque le GC récupère l'espace d'une instance d'emballage, le finaliseur fait en sorte que la ressource est correctement retournée au système d'exploitation.
Cela semble très bien, mais comme d'autres l'ont déjà souligné, l'inconvénient est que la finalisation est intrinsèquement peu fiable: vous ne savez pas quand le finalizer sera exécuté. Pire: il n'y a aucune garantie qu'il sera exécuté du tout. Alors ist mieux pour fournir un mécanisme dispose
et utiliser la finalisation seulement comme filet de sécurité en cas, les clients de votre classe oublient de disposer correctement leurs références:
class Wrapper {
private long handle;
private Handle(long h) {
handle = h;
}
protected void finalize() {
if(handle != 0) returnHandleToOS(handle);
}
public void dispose() {
returnHandleToOS(handle);
handle = 0;
}
private static native long getHandleFromOS();
private static native void returnHandleToOS(long handle);
static Wrapper allocate() {
return new Handle(getHandleFromOS());
}
}
Vous ne pouvez pas dire « si et seulement si »; vous pouvez dire "seulement si", mais il n'y a aucune garantie que ce sera appelé du tout. –
@mmyers: +1. récupéré -> finalize a été exécuté, mais pas l'inverse. – danben
Il n'y a pas de "mais" là: Si le GC tente de récupérer la mémoire, finalize() est appelée. –