java - 如何设计:避免在随机访问文件时资源泄漏

标签 java fault-tolerance

我有客户端/服务器应用程序,客户端应用程序将在其中打开文件。这些文件被分成几块,然后发送到服务器。

客户端不仅发送文件块,还发送其他数据。每个消息(数据或文件块)都有一个优先级。

所有消息根据其优先级进行缓冲并写入TCP流。
当前,无论何时完全发送或接收文件,输入和输出流都会在双方(客户端/服务器)上关闭。这意味着流在发送文件之前一直保持打开状态。


TCP连接被允许失败,因为它将重新连接,并且消息发送将恢复,因此,流将在某个时间点关闭。

但是,例如,如果以及何时杀死JVM,则不会清除流。

我解决此问题的第一个想法是在终结器中添加清理代码,但我知道当JVM被杀死(或如果调用System.exit时),这些代码可能无法运行。

我的第二个想法是重写应用程序的一部分,并且仅在读取/写入一个块所需的时间内使用流。因此,我最终将打开/关闭文件的次数与块的数量一样多。这种方法的优点是允许我使用try-catch-finally结构,但是我有一种直觉要打开和关闭文件,这通常意味着相当多的开销。

当设计不允许finally {}块时,如何清理资源?还是应该避免这种设计,也许采用与我描述的方式类似的方式?

我还担心打开的文件数是否有优先级(理论上是无限的)。

大多数文件通常只有几个KiB,但是在某些情况下,它们可能会变成几个GB。

预先感谢您的输入。

编辑:添加图像

最佳答案

如果我正确理解了这个问题,则您担心如果JVM以不受控制的方式终止,可能无法正确清理。

尽管这是一个普遍的问题,但在您的特定情况下,我认为没有任何问题可担心。您仅打开文件进行读取,因此没有持久状态可能导致应用程序中断(据我所知,TCP连接的另一端可以正常处理断开连接)。文件打开是应用程序内部的一种状态。如果该应用程序被杀死,则操作系统将负责清理其内部可能用于处理该文件上的操作的所有锁或任何数据结构。这意味着在OS内核中不会留下任何“垃圾”,虽然它既不是一种优雅的清理方法,也不是建议的清理方法,但是它只是有效的方法(当然,仅在紧急情况下才使用此方法,而不是通常的方法)处理事物)。终止应用程序将自动关闭打开的文件,并且不应使文件进入任何不一致的状态。

当然,如果该应用被终止,它将不会完成其正在执行的任何操作。这可能会导致应用程序级别状态不一致或某些应用程序逻辑出现其他问题,但仍然不会损害任何操作系统级别的架构(假设操作系统不是越野车)。您可能会丢失数据或破坏应用程序的状态,但您不应破坏内核中的文件系统或数据。

因此,如果您杀死了一个具有事务样式操作的应用程序(您希望完全执行或根本不执行该操作,但是任何中间状态都不对外界可见),则可能会遇到问题。例如,如果您有一个文件,并且需要用较新的版本替换它。如果您首先截断旧文件,然后向其写入新内容(这是显而易见的方式),则如果您的应用程序在截断文件后被杀死,但在写入新内容之前就陷入了麻烦。但是,为了招致这种风险,您需要可变的状态,即编写一些东西。如果您只阅读东西,那几乎可以肯定是安全的。

如果确实遇到这种情况,则可以采取几种方法。一种方法是使应用程序具有防弹性能,并确保其始终能够很好地清理。实际上,这非常困难。您可以向Java应用程序添加关闭挂钩,该挂钩将在应用程序关闭时执行,但仅适用于受控关闭(例如Linux上的常规kill(SIGTERM))。但是,这不能保护您免受管理员(在Linux上为kill -9),OOM-killer(也为Linux)等强行杀死应用的保护。当然,其他操作系统具有与这些情况相同的条件,或者其他情况下应用以无法控制的方式关闭。如果您不是要编写在受控的硬件和软件环境中运行的实时应用程序,那么几乎不可能阻止应用程序被强制终止并无法运行其清理程序。

因此,一个合理的折衷方案通常是在应用程序中仅采取简单的预防措施(如关机钩子),但要记住,您无法阻止所有操作,因此使手动清除变得可能且容易。例如,对于覆盖文件的解决方案是将操作拆分为:首先将旧文件移动到新名称以保留作为备份(此操作通常在OS级别是原子的,因此很安全),然后编写将文件的新内容恢复为旧名称,然后在检查是否正确写入了新文件之后,删除备份。这样,如果应用在两次操作之间被杀死,则存在一个简单的清除过程:将备份文件移至原始名称,然后恢复为较旧但一致的状态。这可以手动完成(但应记录在案),也可以向应用程序添加清理脚本或特殊的“清理”命令,以使此操作简单,可见并消除过程中人为错误的可能性。假设您的应用程序很少被杀死,这通常比花很多时间试图使应用程序具有防弹效果要更有效,只是要意识到这是不可能的。拥抱失败通常比阻止失败更好(不仅在编程中)。

您仍然可能会在操作系统和硬件级别上感到疲倦,但是使用商用硬件和操作系统确实很难避免这一点。即使您的应用在正确的位置看到了新文件,也可能没有实际将其写入磁盘,并且即使仅驻留在缓存中,如果您的应用被杀死,它也不会丢失,但是如果有人拔出电源,它就会丢失插入机器。但是处理这种失败是完全不同的故事。

长话短说,在您仅读取文件的特定情况下,如果您的应用被终止,则操作系统应执行所有清除操作,而在其他情况下,上面提到了一些技巧。

关于java - 如何设计:避免在随机访问文件时资源泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9852073/

相关文章:

blockchain - 非容错区 block 链网络有什么好处

hardware - 容错(辐射)软核?

java - SharePoint 作为网络驱动器 - 使用 Java 以编程方式获取文档 ID

java - 是否收集了 lambda 垃圾?

java - 是否有用于编写绘图程序的 Java 库?

python - tensorflow 监控 session 使用情况

java.lang.LinkageError : ClassCastException 错误

java - 在recyclerview中打开项目后分页不起作用 - Android

java - WLP Microprofile 容错舱壁实现未启动

parallel-processing - 用于低耦合并行进程的 OpenMPI 自定义容错