Nei*_*aft 6 java garbage-collection memory-management finalizer
我一直在阅读这些关于Java终结器的幻灯片.在其中,作者描述了一个场景(在幻灯片33上),该场景CleanResource.finalize()
可以由终结器线程运行,同时CleanResource.doSomething()
仍然在另一个线程上运行.怎么会发生这种情况?
如果doSomething()
是非静态方法,那么执行该方法某人,某处必须有一个强引用它...对吗?那么在方法返回之前如何清除这个引用呢?另一个线程可以突然进入并将该引用置空吗?如果发生这种情况,doSomething()
仍会在原始线程上正常返回?
这就是我真正想知道的,但是对于一个非常超越的答案,你可以告诉我为什么doSomething()
幻灯片38比doSomething()
幻灯片29 更好.为什么仅仅调用这个keepAlive()
方法就足够了?你不需要把整个电话包裹myImpl.doSomething()
在一个synchronized(this){}
街区吗?
编辑3:
结果是终结器和常规方法可以在同一个实例上同时执行。这是如何发生这种情况的解释。代码本质上是:
class CleanResource {
int myIndex;
static ArrayList<ResourceImpl> all;
void doSomething() {
ResourceImpl impl = all.get(myIndex);
impl.doSomething();
}
protected void finalize() { ... }
}
Run Code Online (Sandbox Code Playgroud)
给定这个客户端代码:
CleanResource resource = new CleanResource(...);
resource.doSomething();
resource = null;
Run Code Online (Sandbox Code Playgroud)
这可能会被 JIT 成类似伪 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;
Run Code Online (Sandbox Code Playgroud)
像这样执行的,res
在内联完成后被清除CleanResource.doSomething()
,因此直到该方法执行完成后才会发生GC。Finalize 不可能与同一实例上的另一个实例方法同时执行。
但是,在该点之后不会使用写入res
,并且考虑到没有栅栏,它可以在执行过程中提前移动到写入之后:
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()
Run Code Online (Sandbox Code Playgroud)
在标记的位置 (<---),没有对 CleanResource 实例的引用,因此它适合收集并调用终结器方法。由于在清除最后一个引用后可以随时调用终结器,因此终结器和其余部分可以CleanResource.doSomething()
并行执行。
EDIT2: keepAlive() 确保this
在方法末尾访问指针,以便编译器无法优化指针的使用。并且保证此访问按照指定的顺序发生(同步字标记了一个栅栏,不允许在该点之前/之后重新排序读取和写入。)
原帖:
该示例表示调用了 doSomething 方法,一旦调用,this
就可以提前读取通过指针引用的数据(myIndex
在示例中)。一旦读取了引用的数据,this
该方法中就不再需要指针,并且 CPU/编译器可能会覆盖寄存器/声明该对象不再可访问。因此,GC 可以在对象的 doSomething() 方法运行的同时并发调用终结器。
但由于this
没有使用指针,因此很难看出这将如何产生任何实际效果。
编辑:好吧,也许如果有指向通过缓存访问的对象字段的缓存指针,从this
回收之前计算,然后回收该对象,则内存引用将变得无效。我内心深处很难相信这是可能的,但话又说回来,这似乎确实是一个棘手的极端情况,而且我认为 JSR-133 中没有任何内容可以默认防止这种情况发生。这是一个对象是否被认为仅通过指向其基址的指针引用或通过指向其字段的指针引用的问题。