【问题标题】:Java Implementation of *nix sync() without a system call没有系统调用的 *nix sync() 的 Java 实现
【发布时间】:2012-11-01 09:41:02
【问题描述】:

我正在努力从现有 Java 代码库中删除所有系统调用。我们在商业提供的闭源 JVM 中运行我们的应用程序。当 JVM 通过 getRuntime.exec() 进行系统调用时,java 调用整个 JVM 进程分叉,这会导致严重的性能损失。我们在 linux 平台上运行,但理想情况下尽量保持可移植性。

我在替换我们当前通过 getRuntime.exec() 方法使用的 sync() 调用时遇到了问题。我知道有thissync() 方法和flush()。基于this post,我希望与所有打开的文件流进行同步和刷新。

我的问题是我不直接知道那里有哪些文件流和描述符。我认为解决这个问题的一种方法是检查 /proc/(jvm 进程号)/fd 文件夹,但我找不到使用纯 java 可靠地获取 JVM 进程号的好方法。我认为我可能能够获取某个类(FileDescriptor 类)的所有对象,但从我正在阅读的内容来看,这也是不可行的。

有人对如何在纯 java 中复制 *nix sync() 调用有建议吗?

【问题讨论】:

  • 不会是fork造成性能问题,是文件同步操作。如果你以另一种方式做到这一点,你仍然会遇到同样的问题。
  • 我也建议以下链接,它来自 android 开发人员,但由于我们正在谈论的是 Linux / Java 实现 - 它包含相关信息。 android-developers.blogspot.se/2010/12/saving-data-safely.html
  • @auselen - 抱歉,我的原始帖子在这方面并不完全清楚。问题与这样一个事实有关,即在应用程序启动时,我们在假设整个进程可能被分叉的情况下保留可用内存。这实际上将我们的可用内存减少了一半,从而影响了整体性能。
  • @eharik - 你根本不需要这样做处理System.exec()。 JVM 将使用“vfork”而不是“fork”,并且“vfork”系统调用后跟“exec”系统调用执行最少的复制。
  • @StephenC - 这是真的吗?在这个问题上似乎有一些conflictinginformation。在我们使用系统调用的 JVM 实现中,请尝试分配与父进程相同的内存量。我刚刚运行了一个简单的测试,我将超过 50% 的系统内存分配给了 java 进程并运行了 system.exec() 并且应用程序崩溃了。

标签: java system-calls


【解决方案1】:

您正在做的是 更多 sync 电话。您正在尝试执行“刷新所有文件缓冲区和sync”操作。你在 C / C++ 中也很难做到这一点。

除了找到所有打开的文件的问题(你可能可以解决......),还有一个更大的问题;即是否是正确的时间刷新缓冲区。

假设您的应用程序是多线程的,并且一个线程负责调用sync。该线程如何知道正在写入文件的其他线程已达到文件的一致点?即如果应用程序被杀死并重新启动,(假设)刷新的文件将包含应用程序的逻辑一致状态?答案是(很可能)它知道。所以......事实上......如果应用程序在同步之前刷新,它并没有处于明显更好的位置。

还有另一个问题。假设线程 A 负责刷新/同步,而线程 B 正在愉快地写入某个输出流。考虑这个时间序列:

  1. 线程 A 刷新文件
  2. 线程 B 写入文件
  3. 线程 A 调用同步

避免这种情况的唯一方法是让线程 A 同步并阻止所有其他正在写入文件的线程...它执行刷新和同步之前。

我的建议是只进行同步,而忘记刷新。以经典方式处理文件不一致的问题(通过让应用程序写入临时文件,并进行原子重命名),或者让同步线程与写入文件的线程协调......以便它只有在关键文件一致时才会“同步”。

【讨论】:

  • 感谢您的意见。根据您的回复以及与我团队成员的一些讨论,我认为调用线程将需要提供文件流对象并负责在适当时调用同步。正如您所指出的,从资源的角度来看,文件系统范围的同步是浪费的,从 I/O 完整性的角度来看是有问题的。
猜你喜欢
  • 1970-01-01
  • 2012-09-28
  • 2018-04-04
  • 2016-09-22
  • 2016-01-18
  • 2012-08-22
  • 1970-01-01
  • 2016-05-25
  • 1970-01-01
相关资源
最近更新 更多