Java GC 问题 : How could an object become unreachable while one of its methods is still being executed?

标签 java memory-management garbage-collection finalizer

我一直在读these slides关于 Java 终结器。在其中,作者描述了一个场景(在幻灯片 33 上),其中 CleanResource.finalize() 可以由终结器线程运行,而 CleanResource.doSomething() 仍在运行另一个线程。怎么会这样?

如果 doSomething() 是一个非静态方法,那么要执行该方法的某个人,某个地方必须对其有强引用...对吗?那么如何在方法返回之前清除这个引用呢?另一个线程能否突然进入并清空该引用?如果发生这种情况,doSomething() 是否仍会在原始线程上正常返回?

这就是我真正想知道的,但是对于真的超越的答案,你可以告诉我为什么幻灯片 38 上的 doSomething() 更好比幻灯片 29 上的 doSomething() 更重要。为什么仅调用此 keepAlive() 方法就足够了?您不需要将对 myImpl.doSomething() 的整个调用包装在 synchronized(this){} block 中吗?

最佳答案

编辑3:

结果是终结器和常规方法可以在同一个实例上同时执行。这是如何发生的解释。代码本质上是:

class CleanResource {
   int myIndex;
   static ArrayList<ResourceImpl> all;

   void doSomething() {
     ResourceImpl impl = all.get(myIndex);
     impl.doSomething();
   } 

   protected void finalize() { ... }
}

给定此客户端代码:

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

这可能会被 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;

这样执行,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()

在标记的位置 (<---),没有对 CleanResource 实例的引用,因此它符合收集条件并调用终结器方法。由于可以在最后一个引用被清除后随时调用终结器,因此终结器和 CleanResource.doSomething() 的其余部分可以并行执行。

EDIT2:keepAlive() 确保在方法结束时访问this 指针,因此编译器无法优化指针的使用。并且这种访问保证按照指定的顺序发生(同步字标记了一个栅栏,不允许在该点之前/之后重新排序读写。)

原帖:

这个例子是说doSomething方法被调用了,一旦被调用,通过this指针引用的数据就可以被提前读取(例子中的myIndex)。读取引用数据后,该方法中不再需要 this 指针,并且 cpu/编译器可能会覆盖寄存器/声明对象不再可访问。因此,GC 可以在对象的 doSomething() 方法运行的同时并发调用终结器。

但由于未使用 this 指针,因此很难看出这将如何产生任何实际效果。

编辑:好吧,也许如果有缓存的指针指向通过缓存访问的对象的字段,在它被回收之前从 this 计算,然后回收对象,内存引用变得无效.有一部分人很难相信这是可能的,但话又说回来,这似乎是一个棘手的极端情况,我认为 JSR-133 中没有任何内容可以默认阻止这种情况的发生。这是一个对象是否被认为仅由指向其基址的指针或指向其字段的指针引用的问题。

关于Java GC 问题 : How could an object become unreachable while one of its methods is still being executed?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3363064/

相关文章:

iphone - 使用 UIImage 时的内存管理

c - 使用 mremap 重新分配内存

iphone - 发送 -release 或 -drain 到自动释放池有什么区别?

java - 是什么导致 JVM 进行主要的垃圾收集?

java - 在标记-清除-压缩的紧凑阶段之后,空闲内存块是否会进入伊甸园?

javascript - 我可以触发 JavaScript 的垃圾回收吗?

java - 不显示没有标题的网络推送通知

java - 如何模拟 Map 的返回值?

java - 为什么 Java 8 Optional 实现为 final,没有 Some 和 None 层次结构?

java - 在xml中添加xmlns =""