java(JVM)结束时释放JNI资源(Runtime.addShutdownHook)

2022-05-07 10:13:01 浏览数 (1)

如下代码中cmjnidrv是一个动态库,CodeCacheManager在类加载时就自动将cmjnidrv加载进来。cmjnidrv中有多个独立的线程在运行并申请了大量的内存. release()是个native方法,用于释放cmjnidrv中的申请资源并中止cmjnidrv中的线程。 为了保证在java应用结束的时候cmjnidrv的资源能被正确释放,就必须确保release()被调用。

代码语言:javascript复制
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()方法。

代码语言:javascript复制
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();
}

0 人点赞