我将实现一个典型的 native 库加载。目标进程:
- 从 jar 中提取 native 库
- 将其放在唯一的临时目录中
- 将 native 库加载到 JVM
核心问题是删除临时提取的 native 库文件。 DELETE_ON_EXIT 方法不起作用。原因是,如果库没有从 JVM 中卸载,则文件无法删除。但在ClassLoader被垃圾回收之前不会被卸载。
我读到的一个技巧是使用自定义类加载器( http://www.codethesis.com/blog/unload-java-jni-dll )。我使用自定义 ClassLoader 实现了一个简单的测试,但它不会对自定义 ClassLaoder 进行垃圾收集。以下是示例代码:
自定义类加载器
package minloader;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class NativeLibraryLoaderClassLoader extends ClassLoader
{
@Override
public Class<?> findClass(final String name) throws ClassNotFoundException
{
try
{
final byte[] classData = loadClassData(name);
final Class<?> clazz = defineClass(name, classData, 0, classData.length);
resolveClass(clazz);
return clazz;
}
catch (final IOException ex)
{
throw new ClassNotFoundException("Class [" + name+ "] could not be found", ex);
}
}
/**
* Loads the class file into <code>byte[]</code>.
* @param name The name of the class e.g. de.sitec.nativelibraryloadert.LoadEngine}
* @return The class file as <code>byte[]</code>
* @throws IOException If the reading of the class file has failed
* @since 1.0
*/
private static byte[] loadClassData(String name) throws IOException
{
try(final BufferedInputStream in = new BufferedInputStream(
ClassLoader.getSystemResourceAsStream(name.replace(".", "/")
+ ".class"));
final ByteArrayOutputStream bos = new ByteArrayOutputStream())
{
int i;
while ((i = in.read()) != -1)
{
bos.write(i);
}
return bos.toByteArray();
}
}
@Override
public String toString()
{
return NativeLibraryLoaderClassLoader.class.getName();
}
@Override
public void finalize() {
System.out.println("A garbage collected - LOADER");
}
}
原生界面
package minloader;
/**
*
* @author RD3
*/
public interface Native
{
public boolean initializeAPI();
}
原生实现
package minloader;
public class NativeImpl implements Native
{
/**
* Initializes the NativeImpl API
*
* @return a boolean to indicate if API is successfully loaded
*/
@Override
public boolean initializeAPI(){return true;}
@Override
public void finalize() {
System.out.println("A garbage collected - Native");
}
主要
package minloader;
/**
*
* @author RD3
*/
public class MinLoader
{
/**
* @param args the command line arguments
*/
public static void main(String[] args)
{
NativeLibraryLoaderClassLoader nl = null;
Class pc = null;
Native pcan = null;
try
{
nl = new NativeLibraryLoaderClassLoader();
pc = nl.findClass("minloader.NativeImpl");
pcan = (Native)pc.newInstance();
}
catch (Exception ex)
{
ex.printStackTrace();
}
finally
{
System.out.println("CLEAN UP");
if(pcan != null)
{
pcan = null;
}
if(pc != null)
{
pc = null;
}
if(nl != null)
{
nl = null;
}
System.gc();
System.gc();
System.gc();
try
{
Thread.sleep(10);
}
catch (InterruptedException ex)
{
ex.printStackTrace();
}
System.out.println("CLEANED");
}
try
{
Thread.sleep(10000);
}
catch (InterruptedException ex)
{
ex.printStackTrace();
}
System.out.println("Finished");
}
}
如果我删除行 pcan = (Native)pc.newInstance();
那么自定义 ClassLoder 将进行垃圾收集。
出了什么问题?
问候
最佳答案
没有办法做你想做的事。您可以尝试在 System.gc() 之间混合 runFinalization() 调用,但最终仍然不能保证 ClassLoader 会被垃圾回收。
(请注意,直接使用 findClass 很尴尬。大概您之所以使用它,是因为该类实际上并未由 NativeLibraryLoaderClassLoader 加载。那是因为您使用的是无参数 ClassLoader 构造函数,该构造函数默认使用应用程序类加载器作为父级。如果您添加 NativeLibraryLoaderClassLoader() { super(null); }
,那么您应该能够切换到 loadClass。)
关于java - 发布类加载器引用失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27066726/