如果我正确理解了这个问题,那么您担心在 JVM 以不受控制的方式终止的情况下无法正确清理。
虽然这是一个普遍问题,但在您的特定情况下,我认为没有问题需要担心。您只打开文件进行阅读,因此您的应用程序不会中断持久状态(据我了解,TCP 连接的另一端可以优雅地处理断开连接)。打开的文件是应用程序内部的一种状态。如果应用程序被杀死,操作系统会负责清理所有锁或它可能在内部用于处理对该文件的操作的任何数据结构。这意味着操作系统内核中不会留下任何“垃圾”,虽然它既不是一种优雅的清理方式,也不是推荐的清理方式,但它可以正常工作(当然,仅在紧急情况下使用它,而不是作为正常方式处理事情)。
当然,如果应用程序被杀死,它不会完成它正在执行的任何操作。这可能会导致不一致的应用程序级状态或某些应用程序逻辑的其他问题,但仍然不会损害任何操作系统级的架构(假设操作系统没有错误)。您可能会丢失数据或破坏应用程序的状态,但您不应该破坏内核中的文件系统或数据。
因此,如果您终止具有事务式操作的应用程序(您希望完全执行或根本不执行该操作,但中间状态不应该对外界可见),您可能会遇到问题。例如,如果您有一个文件并且需要将其替换为较新的版本。如果您首先截断旧文件,然后向其写入新内容(这是显而易见的方法),如果您的应用程序在截断文件之后但在写入新内容之前被终止,那么您就有麻烦了。然而,为了引发这样的风险,你需要可变状态,即写一些东西。如果你只看东西,你几乎肯定是安全的。
如果您确实遇到这种情况,您可以采取多种方式。一个是试图使应用程序防弹并确保它总是很好地清理。在实践中,这是非常困难的。您可以向 Java 应用程序添加关闭挂钩,该挂钩将在应用程序关闭时执行,但它仅适用于受控关闭(如kill
Linux 上的常规(SIGTERM))。但是,这并不能保护您免于应用程序被管理员强行杀死(kill -9
在 Linux 上),由 OOM-killer(也是 Linux)等。当然,其他操作系统也有这些情况的等价物或应用程序以无法控制的方式关闭的其他情况。如果您没有编写在受控硬件和软件环境中运行的实时应用程序,那么几乎不可能阻止应用程序被强制终止并阻止其运行其清理程序的所有方式。
因此,合理的妥协通常是在应用程序中仅采取简单的预防措施(如关闭挂钩),但请记住,您不能阻止所有事情,因此使手动清理成为可能和容易。例如,覆盖文件的解决方案是将操作拆分为首先将旧文件移动到新名称以保留作为备份(此操作通常在操作系统级别是原子的,因此是安全的),然后编写将文件的新内容改成旧名称,然后检查新文件是否正确写入后,删除备份。这样,如果应用程序在操作之间被终止,则存在一个简单的清理过程:将备份文件移动到原始名称,从而恢复到较旧但一致的状态。这可以手动完成(但应记录在案),或者您可以向您的应用程序添加清理脚本或特殊的“清理”命令,以使此操作简单、可见,并消除在此过程中出现人为错误的可能性。假设您的应用程序很少被杀死,这通常比花费大量时间试图使应用程序防弹只是意识到它是不可能的更有效。拥抱失败通常比试图阻止它更好(不仅在编程中)。
您仍然可能在操作系统和硬件级别上被烧毁,但对于商品硬件和操作系统来说,这真的很难预防。即使您的应用程序在正确的位置看到新文件,它也可能没有实际写入磁盘,并且如果它仅驻留在缓存中,如果您的应用程序被杀死,它不会丢失,但如果有人拔掉电源,它会丢失插上机器。但处理这种失败是完全不同的故事。
长话短说,在您只读取文件的特定情况下,如果您的应用程序被杀死,操作系统应该执行所有清理,对于其他情况,上面提到了一些提示。