如何关闭AutoClosable类型的静态变量?

How to close a static variable of AutoClosable type?

提问人:Bolpat 提问时间:11/11/2023 最后编辑:Bolpat 更新时间:11/13/2023 访问量:55

问:

我是一个 Java 库的作者,该库提供对我们公司销售的 C++ 库的 Java 访问。其中一个类的实例在以下意义上“拥有”C++ 对象:该类具有一些私有字段,这些字段由某些方法设置,并具有指向 C++ 堆的(唯一)指针的角色。由于 C++ 堆不是垃圾回收的,因此当拥有 Java 对象不再需要内存时,必须手动释放内存,尤其是当不再需要拥有 Java 对象本身时,因此该类实现并释放实例拥有的所有 C++ 内存。理想情况下,用户将在该类的实例上使用 -with-resources。longnativeAutoClosableclose()try

一位客户抱怨说,SonarQube 警告他们没有被调用来释放资源,并建议在对象上使用 -with-resources,但该对象由变量或等效物持有,并且一直存在到应用程序关闭(据我所知)。我想帮助他们,但删除(正如他们建议的那样)根本不正确。问题不在于内存:当应用程序结束并卸载 C++ 库时,内存资源无论如何都会被释放(由操作系统释放)。close()trystaticAutoClosable

那么,如何关闭静态变量所持有的对象,最好是以 SonarQube 检测的方式呢?Java 似乎没有可以使用的类初始值设定项的对立面。AutoClosable

我无法访问 SonarQube 来玩一玩,看看什么可以工作,即当 SonarQube 识别出将被调用时。我将告诉他们,他们应该适当地重新配置 SonarQube 或禁止显示警告。问这个问题,我想确保它本质上是最好的行动方案。 如果这是真的,“是的,是的”将是一个合适的答案。当然,不使用全局状态,即没有全局状态是不费吹灰之力的,但我想他们已经知道了。close()staticAutoClosable

似乎没有人遇到对象的问题,因为在 Stack Overflow 上没有关于它的问题。 我认为这类似于 .NET (C#),我发现这个问题非常多,但是答案特定于用例和 .NET,也没有提供通用解决方案。staticAutoClosableAutoClosableIDisposable

java static sonarqube try-with-resources autocloseable

评论

1赞 Mark Rotteveel 11/11/2023
注册一个关机挂钩以关闭它。也就是说,根据具体情况,它可能不值得付出努力,因为一旦应用程序退出,大多数资源和本机资源无论如何都会被关闭和丢弃。只有当它是具有远程对等体(例如数据库连接)的资源时,才建议关闭它,以便远程也可以立即进行清理。或者,您需要重写代码,使可自动关闭的实例字段成为由 main 方法启动的类的一部分,并在退出后由 main 关闭。AutoCloseable
4赞 Mark Rotteveel 11/11/2023
此外,SonarQube 只是一个使用启发式方法警告您潜在问题的工具。如果你能提供一个很好的理由来解释它无关紧要,那么忽略它的建议应该没有问题。
0赞 Bolpat 11/11/2023
不知道关机钩子。我会调查的。
0赞 rzwitserloot 11/11/2023
@MarkRotteveel这真的是一个糟糕的建议。有效地这样做总是错误的。只有两种选择:[1] 在系统关闭时,您的资源会自动清理,而无需运行任何代码,或者 [2] 程序发生可怕的崩溃灾难:如果有人绊倒了电源线,或者硬杀死了您的应用程序,您的应用程序现在已最终损坏,只能通过完全重新安装来挽救, 或者,必须重新启动操作系统。即使制作关闭钩子也只是打开了编写糟糕代码的大门。
1赞 rzwitserloot 11/11/2023
幸运的是,所有相关资源实际上都是“好的”——如果你的 JVM 退出了你,它们就会关闭,很好。无需使用关闭钩子来关闭文件、网络套接字和 jdbc 连接对象。请注意,如果您“很好地”要求 JVM 退出(没有 、 或 ),则最终的块实际上都不会运行,并且不会调用您在 try-with 块中打开的任何内容。故意。因为没有必要,而且尝试实际上是一个坏主意。kill-9System.exit(0)close()

答:

5赞 rzwitserloot 11/11/2023 #1

一位客户抱怨说,SonarQube 警告他们 close() 不是为了释放资源而调用的,并建议在对象上使用 try-with-resources,但该对象由静态变量或等效物保存,并一直存在到应用程序关闭(据我所知)。

“医生,我把锤子砸到脸上很痛!”

那就别再这样做了。

SonarQube 是一个工具。它可以配置为只说傻话,类似于那把锤子。解决方法不是尝试长出防锤面。解决方法是简单地停止做唯一目的是伤害的事情。

不幸的是,您的客户可能不愿意听到这一点。

但如果是这样,他们可以告诉声纳库贝停止这样做。他们甚至可以添加一个简单的评论,告诉 sonarqube 不要抱怨那个特定的情况。

似乎没有人遇到静态 AutoClosable 对象的问题,因为在 Stack Overflow 上没有关于它的问题。

想要它是一件奇怪的事情,即使是你的客户。如果有一个全局变量具有这种状态,这意味着你的客户的代码是不可测试的(例如,他们不能真正用虚拟实现来替换它)——但是,同样,考虑到它是客户,他们可能不愿意听到这一点。

通常,在应用启动后不久创建一次资源,并将其交给 try-with 块中的其余代码,除非应用退出,否则该块永远不会退出:

public static void main(String[] args) {
  try (var out = new FileOutputStream(args[0])) {
    yourActualApp(out);
  }

类似于上面的东西。这同样适用于您的图书馆。但是,您的客户必须希望摆脱静态的全局字段,并将资源传递到任何地方才能实现此工作,而他们可能不想这样做。

一个可能的解决方案

将代表资源的任何 java 类拆分为 twain。2 个类,这两个类都只是围绕实际实现。一个实现 AutoClosable,另一个不实现。将不“GlobalFoo”的名称命名为“GlobalFoo”,其中 Foo 是您当前的名称 - 并在其 javadoc 中输入此类内容的目的是它在 JVM 的生命周期中存在。这样一来,你的用户必须明确地选择“nono this one lives forever, no need to close it!”,你可以添加一些智能,比如 - 如果它是一个具有唯一键的资源(比如说,它代表一个文件。 文件具有唯一键: 完全限定的文件名! - 您可以存储这些唯一键,如果库的用户尝试使用相同的键创建这样的全局对象,则抛出错误键不止一次。

您可以让一个扩展另一个 - 该扩展器只是添加 AutoClosable,仅此而已。

评论

1赞 khachik 11/11/2023
这让我的一天变得:)
1赞 John Bollinger 11/11/2023 #2

一位客户抱怨说,SonarQube 警告他们 close() 不是为了释放资源而调用的,并建议在对象上使用 try-with-resources,但该对象由静态变量或等效物保存,并一直存在到应用程序关闭(据我所知)。

如果 SonarQube 是可信的,则用户正在创建类的实例,该实例不受 try-with-resources 语句管理,并且他们没有显式管理。然后有两种可能性:close()

  1. 对于特定类的实例来说,这是可以的,或者
  2. 这是一个使用错误。

无论哪种方式,从根本上说,这都是一个“他们”的问题,而不是“你”的问题。

我想帮助他们,但删除 AutoClosable(正如他们建议的那样)根本不正确。

我希望删除(以及您显式实现的任何子接口)将使 SonarQube 静音,因为它将不再将对象识别为其方法相关的对象。我倾向于相信你,那是不正确的。当然,这将使该类无法与 try-with-resources 语句一起使用。这不会使 SonarQube 目前报告的用法比现在更正确或更不正确。AutoClosableclose()

问题不在于内存:当应用程序结束并卸载 C++ 库时,内存资源无论如何都会被释放(由操作系统释放)。

这往往支持一个前提,即上面的 (1) 适用于您的类。但是,未关闭对象还存在其他潜在问题。例如,如果类的实例管理可修改的文件,则无法关闭它们可能意味着如果 VM 异常终止,则某些写入将丢失。但同样,这似乎是一个使用问题,而不是设计或实现问题。

那么,如何关闭静态变量所持有的对象,最好是以 SonarQube 检测的方式呢?Java 似乎没有可以使用的类初始值设定项的对立面。AutoClosable

通过显式调用它们的方法。 在这里无关紧要,除非它声明了该方法。除了与try-with-finally语句的相关性之外,它没有任何特殊的语义。 你是对的,Java 没有任何类型的钩子用于在清理类之前执行代码,也不能保证未使用的类会被清理,甚至在程序终止时也不会。close()AutoCloseable

关于这个问题的评论提到了关机钩子,但这些都是糟糕的juju。此外,我怀疑你在类本身中对它们所做的任何事情都会满足 SonarQube,如果你的用户能够成功地使用它们来满足 SonarQube,我会有点惊讶。即使他们这样做了,这真的会让他们的应用程序以更好或更可靠的方式工作吗?

总的来说,我认为首先将对象存储在字段中通常是一个错误。在某种程度上,有一个合理的预期需要关闭,这很难与字段的典型无限可用性相协调。AutoCloseablestaticAutoClosablestatic

我将告诉他们,他们应该适当地重新配置 SonarQube 或禁止显示警告。

如果你对对象不关闭(完全)是可以的感到满意,那么这正是我会做的。

当然,不使用全局状态,即没有静态 AutoClosable 是不费吹灰之力的,但我想他们已经知道了。

我不会假设他们确实知道这一点,但我也不会假设他们会在被告知他们不应该做他们正在做的事情时做出良好的反应。从用户支持的角度来看,这也不是特别好的外观。