加载到JVM后删除临时文件

6
使用system.loadLibrary()函数加载库文件后立即删除它是否可行?以下是情况说明:
我们使用JNI调用用C语言编写的DomainSocket实用程序来从Java应用程序中调用。我们将.SO文件打包在同一个JAR文件中,并使用一个实用程序在文件系统上创建一个临时文件并使用loadLibrary函数加载.SO文件。我们使用File.createTempFile()函数创建唯一的临时文件。我们使用deteleOnExit()函数在JVM关闭时删除临时文件。
在独立应用程序的JVM上,这个方案很好用。但是,当部署到Web容器(如Tomcat)时,我们可能会有多个应用程序在同一个JVM上运行的问题。首先,为每个部署的应用程序创建一个临时文件。真正的问题是,当停止和重新启动或重新部署应用程序时,该临时文件不会被删除,而是创建一个新的临时文件。所有的临时文件都在JVM关闭时被删除(Tomcat重启)。
我们试图解决的方法是在loadLibrary()函数之后删除临时文件。这似乎很好用。在重启时创建一个新的临时文件,并在加载后立即删除。
想询问有没有人有一些指针或建议?
我们不想使用common/lib机制仅一次加载.SO文件以使这些.SO文件成为应用程序的一部分。
2个回答

1
“使用system.loadLibrary()函数加载库文件后立即删除它,可以吗?” 不行。操作系统随时可以“卸载”库,并可能需要重新从磁盘加载二进制文件。如果您删除该文件(并且该文件实际上未被进程保持打开状态),则会出现一些非常奇怪的行为...可能是像“总线错误”或其他几乎无法解释的情况。
即使库未从内存中删除并从磁盘重新加载,各种操作系统也有“惰性加载”和其他标志,这可能会使您陷入竞争条件的情况,其中您可能在实际加载所有代码之前删除了磁盘上的库文件。
“我们正在使用File.createTempFile()创建唯一的临时文件。我们正在使用deleteOnExit()在JVM关闭时删除临时文件。” 这似乎是一个合理的方法,除了你遇到的问题。除了使用File.createTempFile之外,为什么不在可预测的位置使用可预测的文件名(例如/tmp/libgoodstuff.so)?如果这样做,那么您只需要创建(并安排删除)单个文件(每个要加载的库)。
我想查看是否有人可以提供一些指针/建议?
.so文件放置在应用程序中将很难管理,除非您非常了解目标环境。例如,您需要确保您的目标架构和操作系统正确等。
总之,在Java中作为可分发应用程序的本地库是非常麻烦的,并且除非没有其他选择,否则应该避免使用。

1
当你说它可能随时卸载库时,你指的是哪个操作系统? - apangin
@apangin 我没有特定的操作系统想法。但是,没有POXIX等要求代码保持加载状态,因此基于磁盘的工件可以自由消失。我期望从磁盘加载并删除共享库将导致“未定义行为”。 - Christopher Schultz
嗯,我不清楚操作系统(至少是 Oracle JDK 支持的操作系统)是否会在共享库被删除时导致应用程序崩溃。通常情况下,操作系统要么会拒绝删除(Windows),要么只是取消链接文件名(Linux),但保留磁盘上的文件数据。删除该文件并不影响已打开的文件的内存映射。 - apangin

1
是的,这种方法可能会有用,但有一定的限制。
当调用 System.loadLibrary 时,相应的.so 文件被映射到进程地址空间中。当你删除文件时,相应的 dentry 立即被删除,但不是 inode。也就是说,文件名被取消链接,但文件数据保留,直到释放对文件的最后一个引用为止。
这是包管理器安装更新的方式。即使正在运行的进程使用可执行文件和共享库,它们也可以被删除。操作系统将确保正在运行的程序继续使用旧版本的库,而新程序将加载新版本。
但是,这种方法并不可移植。它在 Linux 上工作,但不适用于其他操作系统和不适用于每个文件系统(例如考虑 Windows 或 FAT)。因此,我不建议这样做,除非你知道正在使用的特定操作系统平台。

我们在Web容器中遇到了一个问题,只有一个.SO文件。由一个Web应用程序加载的.SO文件无法被另一个Web应用程序加载,因为类加载器的原因。Tomcat为每个Web应用程序分配一个唯一的类加载器,多次加载相同的.SO将不起作用... - user2089210
@user2089210 对的,我漏掉了那部分。所以,如果您不需要应用程序在各个平台之间可移植,那么在加载库后立即删除它们是可以的。嗯,拥有JNI库使应用程序无法移植。 - apangin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接