如何确保资源被释放,嵌套对象层次结构?

Posted

技术标签:

【中文标题】如何确保资源被释放,嵌套对象层次结构?【英文标题】:How to make sure resource is released, with nested object hierarchy? 【发布时间】:2015-12-12 15:02:53 【问题描述】:

我有这样的代码(简化):

class A 

    B b = new B();

    void close() 
        b.close();
    


class B 

    Closeable mustBeClosed = new Closeable() 
        
            System.out.println("create");
        
        @Override
        public void close() 
            System.out.println("close");
        
    ;

    int n = 0 / 0;

    void close() 
        mustBeClosed.close();
    


//code
try (A a = new A()) 
    //do something

如何保证mustBeClosed被释放?

当对象层次结构复杂时,可能会发生这种情况。覆盖 B 的 finalize 可能不是一个完美的解决方案。

有针对此问题的最佳实践或原则吗?

修改后的版本如下:

class B 
    Closeable mustBeClosed;
    B() 
        try 

            mustBeClosed = ...

            //other initialization which might raise exceptions

         catch (throwable t) 
            close();
            throw t;
        
    

    void close() 
        if (mustBeClosed != null) 
            try 
                mustBeClosed.close();
             catch (Throwable t) 
            
        
        //all other resources that should be closed
    

然而,这需要太多的代码并且远非优雅。更重要的是,所有权层次结构中的所有类似乎都应该遵循相同的样式,这会导致大量代码。

有什么建议吗?

【问题讨论】:

【参考方案1】:

您的问题是,如果构造函数抛出异常,try-with-resources 将不会(实际上不能)调用close()

任何分配资源的对象构造,并且在构造过程中可能会失败在分配资源之后必须在异常级联之前释放该资源调用堆栈。

两种解决方法:

1) 确保确保资源分配是最后执行的操作。在您的情况下,这意味着将字段 n 移动到字段 mustBeClosed 之前。

2) 在构造函数中而不是在字段初始值设定项中处理资源构造,因此您可以捕获任何后续异常并在重新抛出异常之前再次释放资源,如您的替代解决方案所示。 但是,您不必在 close() 方法中进行 null 检查,因为如果对象构造成功,mustBeClosed 将始终为非 null,而如果对象构造失败,则无法调用 close()

【讨论】:

【参考方案2】:

使用包装器方法关闭所有Closeable 实例优雅地

closeGraceFully(Closeable c)  // call this guy for all instances of closeable
   try
       c.close();
    catch(IOException ex) 
      // nothing can be done here, frankly.
  

然后调用这个包装方法。不要直接打电话给close()。不要使用finalizers 他们是邪恶的,会减慢你的应用程序。

【讨论】:

以上是关于如何确保资源被释放,嵌套对象层次结构?的主要内容,如果未能解决你的问题,请参考以下文章

释放资源

python如何让程序一直运行且内存资源自动释放?

C#.net如何手动释放内存资源

Linux内核(linux-5.2.9)--内核对象(对象的引用计数)

C 语言结构体 ( 结构体中嵌套一级指针 | 分配内存时先 为结构体分配内存 然后再为指针分配内存 | 释放内存时先释放 指针成员内存 然后再释放结构头内存 )

Java程序的优化