如下代码中cmjnidrv
是一个动态库,CodeCacheManager
在类加载时就自动将cmjnidrv
加载进来。cmjnidrv
中有多个独立的线程在运行并申请了大量的内存.
release()
是个native方法,用于释放cmjnidrv
中的申请资源并中止cmjnidrv
中的线程。
为了保证在java应用结束的时候cmjnidrv
的资源能被正确释放,就必须确保release()
被调用。
public class CodeCacheManager{
private static final Logger logger = LoggerFactory.getLogger(CodeCacheManager.class);
static{
System.loadLibrary("cmjnidrv");
}
private static final CodeCacheManager instance=new CodeCacheManager();
static public CodeCacheManager getInstance(){
return instance;
}
private CodeCacheManager(){
}
/**
* 释放所有JNI资源
*/
public native static void release();
/* (非 Javadoc)
* @see java.lang.Object#finalize()
*/
@Override
protected void finalize() throws Throwable {
logger.info("release JNI resoure...");
release();
logger.info("release JNI resoure finished");
}
}
原本的设计是在finalize()
方法中执行release()
方法。这样CodeCacheManager
对象被垃圾回收器回收的时候会自动释放JNI资源,但是CodeCacheManager
是个单例(singleton)的class,instance
静态成员变量永远保持着对单例对象的引用,所以JAVA的垃圾回收器永远不可能去调用finalize()
。
所以这个方案不可行,事实也是正是这样,执行shutdown.sh停止tomcat服务器时,执行top命令显示,java
进程仍然在运行中,而且占用着大量的内存。也没有log “release JNI resoure…”输出,显示finalize()方法没有被执行。
所以如何在JVM结束时释放静态加载的动态库中的资源还是得另想办法。
这时 void java.lang.Runtime.addShutdownHook(Thread hook)
就派上用场了。下面是addShutdownHook
方法的原文说明(部分):
Registers a new virtual-machine shutdown hook. The Java virtual machine shuts down in response to two kinds of events: The program exits normally, when the last non-daemon thread exits or when the exit (equivalently, System.exit) method is invoked, or The virtual machine is terminated in response to a user interrupt, such as typing ^C, or a system-wide event, such as user logoff or system shutdown.
当JVM结束时(正常结束,或被Ctrl-C中止,或因系统事件(如logoff ,shutdown))会执行用addShutdownHook
方法注册的线程。
所以确保静态加载的JNI资源释放的办法,就是在加载动态库的时候,向JVM注册一个hook线程,用于执行release()
方法。
public class CodeCacheManager{
private static final Logger logger = LoggerFactory.getLogger(CodeCacheManager.class);
static{
// add hook for release JNI resource on JVM shutdown
Runtime.getRuntime().addShutdownHook(new Thread (){
@Override
public void run() {
logger.info("release JNI resoure...");
relase();
logger.info("release JNI resoure finished");
}});
System.loadLibrary("cmjnidrv");
}
private static final CodeCacheManager instance=new CodeCacheManager();
static public CodeCacheManager getInstance(){
return instance;
}
private CodeCacheManager(){
}
/**
* 释放所有JNI资源
*/
public native static void release();
}