代码之家  ›  专栏  ›  技术社区  ›  Neil Traft

JAVA GC问题:当一个对象仍在执行时,它如何变得不可达?

  •  6
  • Neil Traft  · 技术社区  · 15 年前

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

    如果 doSomething() 是一个非静态方法,然后在某个地方执行该方法 必须 有很强的参考价值…正确的?那么,如何在方法返回之前清除这个引用呢?另一条线是否会突然进入并使引用无效?如果发生了,会 剂量学() 仍然在原始线程上正常返回?

    我只想知道这些,但为了 真正地 你可以告诉我为什么 剂量学() 在幻灯片38上比 剂量学() 在幻灯片29上。为什么仅仅调用这个就足够了 keepAlive() 方法?你不需要把整个电话都给 myImpl.doSomething() 在一个 synchronized(this){} 街区?

    1 回复  |  直到 15 年前
        1
  •  3
  •   mdma    15 年前

    EdTe3:

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

    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; 
    

    这可能会被抖动到类似于伪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。在同一实例上,不可能与另一个实例方法同时完成执行。

    但是,写信给 物件 在该点之后不使用,并且考虑到没有围栏,可以在执行过程中提前移动,在写入之后立即移动:

    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实例的引用,因此它符合收集条件,并且可以调用Finalizer方法。由于在清除最后一个引用之后的任何时候都可以调用终结器,因此终结器和 cleanresource.dosomething()。 并行执行。

    edit2:keepalive()确保 this 在方法的末尾访问指针,这样编译器就无法优化指针的使用。并且该访问保证按指定的顺序进行(同步字标记了一个界限,该界限不允许在该点之前/之后重新排序读和写。)

    原始职位:

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

    但自从 指针没有使用,很难看出这将如何产生任何实际效果。

    编辑:好吧,也许如果有缓存指针指向通过缓存访问的对象字段,从 在它被回收之前,对象随后被回收,内存引用将变为无效。我有一部分人很难相信这是可能的,但再一次,这似乎是一个棘手的角落情况,我认为在JSR-133中没有任何东西可以阻止这种情况的默认发生。这是一个问题,一个对象是只被指向它的基的指针引用,还是被指向它的字段的指针引用。