Java GC question: How can an object become inaccessible while one of its methods is still executing?

I read these slides about Java finalizers. In it, the author describes a script (on slide 33) whereby CleanResource.finalize() can be executed by a finalizer thread, and CleanResource.doSomething() is still running in another thread. How could this happen?

If doSomething() is a non-static method, then for this method to run, someone must have a strong reference to it somewhere ... right? So how can this link be cleared before the method returns? Can another thread pounce on this link? If this happens, will doSomething() still return normally to the original thread?

That's all I really want to know, but for a really higher and higher answer, you can tell me why doSomething() on slide 38 is better than doSomething() on slide 29. Why is it enough to just call this keepAlive() method? You would not need to wrap the whole call up to myImpl.doSomething() in a synchronized(this){} block?

+6
java garbage-collection memory-management finalizer
source share
1 answer

EDIT3:

The result is that the finalizer and the regular method can be executed simultaneously in the same instance. Here is an explanation of how this can happen. Essential Code:

 class CleanResource { int myIndex; static ArrayList<ResourceImpl> all; void doSomething() { ResourceImpl impl = all.get(myIndex); impl.doSomething(); } protected void finalize() { ... } } 

Given this client code:

 CleanResource resource = new CleanResource(...); resource.doSomething(); resource = null; 

It could be jited by something like this 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; 

It is executed in this way, res is cleared after the completion of inlined CleanResource.doSomething() , so gc will not be executed until this method completes the execution. There is no way to complete execution simultaneously with another instance method in the same instance.

But the record in res not used after this point, and provided that there are no barriers, it can be transferred earlier during execution, immediately after recording:

 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() 

In the marked place (<---), there are no references to the CleanResource instance, and therefore it has the right to collect and the finalizer method called. Since the finalizer can be called anytime after clearing the last link, it is possible that the finalizer and the rest of CleanResource.doSomething() are executed in parallel.

EDIT2: KeepAlive () ensures that the this pointer is available at the end of the method so that the compiler cannot optimize the use of the pointer. And that this access is guaranteed to be performed in the specified order (a synchronized word denotes a fence that prohibits the re-reading and writing order before / after this point.)

Original post:

The example says that the doSomething method is called and once called, the data pointed to by the this pointer can be read earlier ( myIndex in the example). After reading the reference data, the this pointer is no longer needed in this method, and cpu / compiler can overwrite registers / declare the object as no longer available. Thus, the GC can then call the finalizer at the same time as the doSomething () method.

But since the this pointer is not used, it is difficult to understand how this will have a tangible effect.

EDIT: It is possible if the object fields accessed via the cache have cached pointers computed from this before they are restored and then the object is restored, memory references will become invalid. There is a part of me that hardly believes in this, perhaps, but again, this seems like a difficult corner case, and I donโ€™t think there is anything in JSR-133 to prevent this by default. This is a question of whether an object is considered a link only with pointers to its base or pointers to its fields.

+3
source share

All Articles