EDIT3:
Le résultat est que le finaliseur et une méthode régulière peuvent être exécutées simultanément sur la même instance. Voici une explication de comment cela peut arriver. Le code est essentiellement:
class CleanResource {
int myIndex;
static ArrayList<ResourceImpl> all;
void doSomething() {
ResourceImpl impl = all.get(myIndex);
impl.doSomething();
}
protected void finalize() { ... }
}
Vu le code client:
CleanResource resource = new CleanResource(...);
resource.doSomething();
resource = null;
Cela pourrait être JITed à quelque chose comme ce pseudo C
register CleanResource* res = ...; call ctor etc..
// inline CleanResource.doSomething()
register int myIndex = res->MyIndex;
ResourceImpl* impl = all->get(myInddex);
impl->DoSomething();
// end of inline CleanResource.doSomething()
res = null;
Exécuté comme ça, res
est effacé après la Inline CleanResource.doSomething()
est fait, donc le gc ne se produira qu'après que la méthode ait fini d'être exécutée. Il n'y a aucune possibilité de finaliser l'exécution concurremment avec une autre méthode d'instance sur la même instance.
Mais, l'écriture à res
n'est pas utilisé après ce moment-là, et étant donné qu'il n'y a pas de clôtures, il peut être déplacé plus tôt dans l'exécution, immédiatement après l'écriture:
register CleanResource* res = ...; call ctor etc..
// inline CleanResource->doSomething()
register int myIndex = res->MyIndex;
res = null; /// <-----
ResourceImpl* impl = all->get(myInddex);
impl.DoSomething();
// end of inline CleanResource.doSomething()
Au marqué emplacement (< ---), il n'y a aucune référence à l'instance CleanResource, et donc il est éligible pour la collecte et la méthode finalizer appelée. Comme le finaliseur peut être appelé à tout moment après l'effacement de la dernière référence, il est possible que le finaliseur et le reste du CleanResource.doSomething()
s'exécutent en parallèle.
EDIT2: keepAlive() garantit que le pointeur this
est accessible à la fin de la méthode, de sorte que le compilateur ne peut pas optimiser l'utilisation du pointeur. Et que cet accès est garanti à se produire dans l'ordre spécifié (le mot synchronisé marque une clôture qui interdit réordonnancement des lectures et écritures avant/après ce point.)
Original Post:
L'exemple dit que la méthode doSomething est appelée, et une fois appelée, les données référencées via le pointeur this
peuvent être lues précocement (myIndex
dans l'exemple).Une fois les données référencées lues, le pointeur this
n'est plus nécessaire dans cette méthode, et le cpu/compilateur peut écraser les registres/déclarer que l'objet n'est plus accessible. Ainsi, le GC pourrait alors appeler simultanément le finaliseur en même temps que la méthode doSomething() de l'objet est en cours d'exécution.
Mais puisque le pointeur this
n'est pas utilisé, il est difficile de voir comment cela aura un effet tangible.
EDIT: Eh bien, peut-être s'il y a des pointeurs mis en cache dans les champs de l'objet accessibles par cache, calculés this
avant d'être récupéré, et l'objet est ensuite récupéré les références de la mémoire deviennent invalides. Il y a une partie de moi qui a du mal à croire que c'est possible, mais là encore, cela semble être un cas délicat, et je ne pense pas qu'il y ait quoi que ce soit dans JSR-133 pour empêcher cela par défaut. Il s'agit de savoir si un objet est considéré comme référencé uniquement par des pointeurs vers sa base ou par des pointeurs vers ses champs.
Est-ce que cela pourrait également se produire sur une machine virtuelle basée sur une pile? Maintenant que je pense au niveau de bytecode comme vous êtes, je comprends mieux. Je suppose que dans le cas d'une machine de pile, la référence n'a pas besoin d'être sur la pile pour que 'invokevirtual' se termine? Donc, une fois que la valeur 'myIndex' est récupérée,' this' peut être retiré de la pile et potentiellement récupéré? –
Sur une implémentation basée sur une pile stricte, cela n'est pas possible puisque le pointeur this restera sur la pile. Mais avec l'inlining de la méthode, l'exécution hors de l'ordre et l'allocation de registre, l'appel simultané au finaliseur devient possible. Voir ma dernière édition dans cette saga continue. :) – mdma
Bonne réponse! Je ne savais même pas que quelqu'un d'autre que Dalvik VM d'Android avait implémenté une JVM basée sur le registre. Est-ce commun? –