浅析Java中的资源关闭

内存是计算机很宝贵的资源,我们在使用资源时如果不关闭打开的资源,就有可能导致内存泄露的风险,下面浅析下Java中几种常见的资源关闭方案

先定义一个资源类表示需要关闭的资源

public class MyResource implements Closeable {     @Override     public void close() throws IOException {         System.out.println("MyResource的close方法被调用!");     } }

一、直接在try块中关闭,如下代码

public class FinallyTest {     public static void main(String[] args) {         try {             MyResource myResource = new MyResource();             System.out.println("抛出异常前...");             int i = 1 / 0;             myResource.close();         } catch (Exception ex) {             ex.printStackTrace();         }     } }

我们可以看到输出的结果是

浅析Java中的资源关闭

其实我们可以看到,当抛出异常后try块中异常后续的代码就不会被执行,如当我们执行代码int i = 1 / 0时,这句代码肯定会抛出异常,当抛出异常后try块中的代码就不会被执行了,即后面的myRosource.close()方法就不会被调用。如果代码中存在大量的资源调用,但是抛出了异常,就会导致内存泄露的风险,其实我们从字节码文件中也可以知道结果字节码如下图所示。

浅析Java中的资源关闭

二、接下来我们看看使用try{...}catch{....}finally{...}的形式,接下来我们把代码改为如下形式,把调用close的代码移动到finally中。

public class FinallyTest {     public static void main(String[] args) throws IOException {         MyResource myResource = null;         try {             myResource = new MyResource();             System.out.println("抛出异常前...");             int i = 1 / 0;         } catch (Exception ex) {             ex.printStackTrace();         } finally {             if (myResource != null) {                 myResource.close();             }         }     } }

输出结果如下,此时我们从输出结果中可以看出,不管try块中是否抛出异常,finally最终都会被执行

浅析Java中的资源关闭

接下来我们从字节码的层面看看调用机制。其实我们可以看到,不管是走哪一个分支,最终都会执行finally的语句

浅析Java中的资源关闭

三、try-with-resources 但是从JDK1.7开始,有新的方式。我们看接口AutoCloseable,是从JDK1.7开始提供的

/*  * @author Josh Bloch  * @since 1.7  */ public interface AutoCloseable {     void close() throws Exception; } 

我们定义如下测试代码

public class FinallyTest {     public static void main(String[] args) throws IOException {         try (MyResource myResource = new MyResource()) {             System.out.println("抛出异常前...");             int i = 1 / 0;         } catch (Exception ex) {             ex.printStackTrace();         }     } }

执行结果如下,我们不需要显示调用close方法,close会被调用,执行结果如下。

浅析Java中的资源关闭

那java是如何实现的呢?我们反编译字节码看看结果,这就是try...catch...finally的形式。try-catch-resource这种写法只是一个语法糖。

浅析Java中的资源关闭

但是细心的朋友可能发现var2.addSuppressed(var11)这个是什么鬼?我们去看看Throwable中方法addSuppressed的注释,如下所示。可以看到,这个方法也是从JDK1.7开始的。省略了部分注释,其实就是为了避免异常覆盖。

/**      * Appends the specified exception to the exceptions that were      * suppressed in order to deliver this exception. This method is      * thread-safe and typically called (automatically and implicitly)      * by the {@code try}-with-resources statement.      * @since 1.7      */     public final synchronized void addSuppressed(Throwable exception) {         if (exception == this)             throw new IllegalArgumentException(SELF_SUPPRESSION_MESSAGE, exception);          if (exception == null)             throw new NullPointerException(NULL_CAUSE_MESSAGE);          if (suppressedExceptions == null) // Suppressed exceptions not recorded             return;          if (suppressedExceptions == SUPPRESSED_SENTINEL)             suppressedExceptions = new ArrayList<>(1);          suppressedExceptions.add(exception);     }

接下来我们把MyResource的代码改一下,让close方法抛出异常。如下所示。

public class MyResource implements Closeable {     @Override     public void close() throws IOException {         System.out.println("MyResource的close方法被调用!");         throw new RuntimeException("我这里抛出异常啦....");     } }

然后我们用传统的try{...}catch{...}finally{...}看看输出结果是什么样的。

public static void main(String[] args) throws Exception {         MyResource myResource = new MyResource();         try {             System.out.println("抛出异常前....");             int i = 1 / 0;         } catch (Exception ex) {             throw ex;         } finally {             if (myResource != null) {                 myResource.close();             }         }     }

输出结果如下。

浅析Java中的资源关闭

发现了吗?堆栈里面没有int i = 1 / 0 ,所产生的异常。异常被屏蔽了,针对这个问题,addSuppressed就可以派上用场啦。更多addSuppressed的使用可参考

接下来我们看看try...with...Resource...的形式的解决方案。代码如下

public static void main(String[] args) throws Exception {         try (MyResource myResource = new MyResource()) {             System.out.println("抛出异常前....");             int i = 1 / 0;         } catch (Exception ex) {             throw ex;         }     }

输出结果如下:

浅析Java中的资源关闭

看到了吗?被append后面啦。

个人学习记录

版权声明:玥玥 发表于 2021-04-18 3:37:22。
转载请注明:浅析Java中的资源关闭 | 女黑客导航