已通过JNA加载并释放的DLL无法删除问题求助
尝试删除通过JNA加载并释放的DLL文件,已经试过Stack Overflow上《How to dispose library loaded with JNA》里的所有方案,但都无效。
未添加延迟的测试代码
import java.io.File; import com.sun.jna.Library; import com.sun.jna.Native; import com.sun.jna.NativeLibrary; class Filter { private static ExtDLLTool DLLUtil; final private static String dllPath = "./ExternalDownloader_64.dll"; static { DLLUtil = (ExtDLLTool) Native.loadLibrary(dllPath, ExtDLLTool.class); } public static void main(String[] args) { if (DLLUtil != null) { DLLUtil = null; NativeLibrary lib = NativeLibrary.getInstance(dllPath); lib.dispose(); } File dllFile = new File(dllPath); if(dllFile.exists()){ boolean isDeleted = dllFile.delete(); if(!isDeleted){ System.out.println("Unable to delete dll file, since it hold by jvm"); } } } private interface ExtDLLTool extends Library { String validateNomination(String dloadProps); } }
添加延迟后的测试代码
为给本地代码时间释放句柄,添加了延迟:
import java.io.File; import java.nio.file.Files; import java.nio.file.Paths; import com.sun.jna.Library; import com.sun.jna.Native; import com.sun.jna.NativeLibrary; class Filter { private static ExtDLLTool DLLUtil; final private static String dllPath = "./ExternalDownloader_64.dll"; static { DLLUtil = (ExtDLLTool) Native.loadLibrary(dllPath, ExtDLLTool.class); } public static void main(String[] args) throws Exception{ if (DLLUtil != null) { DLLUtil = null; NativeLibrary lib = NativeLibrary.getInstance(dllPath); lib.dispose(); Thread.sleep(3000); } File dllFile = new File(dllPath); if(dllFile.exists()){ Files.delete(Paths.get(dllPath)); // boolean isDeleted = dllFile.delete(); if(dllFile.exists()){ System.out.println("Unable to delete dll file, since it hold by jvm"); } } } private interface ExtDLLTool extends Library { String validateNomination(String dloadProps); } }
运行异常
运行上述代码均抛出异常,说明JVM未释放文件:
Exception in thread "main" java.nio.file.AccessDeniedException: .\ExternalDownloader_64.dll at sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:83) at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:97) at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:102) at sun.nio.fs.WindowsFileSystemProvider.implDelete(WindowsFileSystemProvider.java:269)
强制触发垃圾回收
调用dispose()后,除了置空引用,显式触发GC能加快隐式引用的回收。在dispose()后添加:System.gc(); System.runFinalization();配合延迟使用,能提升文件释放的概率。
避免静态加载DLL
静态代码块加载的DLL会绑定到类加载器,类加载器未卸载时DLL句柄不会释放。改成方法内动态加载,让引用更快被回收:public static void main(String[] args) throws Exception { ExtDLLTool dllUtil = Native.loadLibrary(dllPath, ExtDLLTool.class); // 执行DLL调用逻辑 dllUtil = null; NativeLibrary lib = NativeLibrary.getInstance(dllPath); lib.dispose(); System.gc(); System.runFinalization(); Thread.sleep(1000); // 尝试删除文件 }检查DLL内部资源泄漏
可能不是JNA的问题,而是DLL自身打开了文件句柄或其他资源未释放。确认DLL是否有资源释放的导出函数,调用该函数后再尝试删除文件。临时目录加载策略
先把DLL复制到临时目录,加载临时目录的DLL文件,这样原文件从一开始就不会被锁定。使用完成后直接删除临时DLL即可。
内容的提问来源于stack exchange,提问作者Naveen S

