argius note

プログラミング関連

Java SE 7 (2) - リソース自動closeの仕組み

文字列switchに続いて、バイトコードに影響する言語仕様の2つ目、"The try-with-resources Statement"です。
これまでは下のように書いていたものが、

InputStream is = new FileInputStream("a.txt");
try {
    // TODO
} finally {
    is.close();
}

こう書けるようになりました。

try (InputStream is = new FileInputStream("a.txt")) {
    // TODO
}


スコープはtryブロックに閉じるので、同じ変数名で並べることができます。

try (InputStream is = new FileInputStream("a.txt")) {
    // TODO
}
try (InputStream is = new FileInputStream("a.txt")) {
    // TODO
}


また、複数のリソースを同時に書けます。でも、区切り文字が";"なのはちょっとかっこ悪い気が。ここは","じゃない?

try (InputStream is = new FileInputStream("in.txt");
     OutputStream os = new FileOutputStream("out.txt")) {
    // TODO
}


これらは、Java1.7で導入されたAutoCloseable*1インタフェースを実装しているクラスならどれでも使うことが出来ます。今回はCloseableと違い、InputStreamやOutputStreamだけでなくJDBCのConnection,Statement,ResultSetにも適用されています。
もちろん自作クラスにも使えます。


以下の例は、前回と同様にそれぞれJava7とJava6(とそれ以前)で書いた2つのプログラムのバイトコードが同じになるように書いてみたものです。f1がJava7版、f2がJava6版です。
複数リソース版も併記します。
(念のため:コンパイラによっては生成されるコードが異なる可能性があります。今回は本家のJDKを使っています。)
追記@2011-09-10:Throwable#addSuppressedは1.7で追加されたメソッドなので、厳密には1.6ではコンパイルできません。うっかりしてました。

以前から、try-catch-finallyは裏側ではコードが大量生成されていたわけですが、これも元のコードと比べるとかなり大量のバイトコードが生成されていることが分かります。

単一リソース版

final class BogusResource implements AutoCloseable {

    int read() throws Exception {
        return 0;
    }

    public void close() throws Exception {
    }

    public static void f1() throws Exception {
        try (BogusResource resource = new BogusResource()) {
            resource.read();
        }
    }

    public static void f2() throws Exception {
        BogusResource resource = new BogusResource();
        Throwable th = null;
        try {
            resource.read();
        } catch (Exception ex) {
            th = ex;
            throw ex;
        } finally {
            if (resource != null) {
                if (th != null) {
                    try {
                        resource.close();
                    } catch (Exception ex) {
                        th.addSuppressed(ex);
                    }
                } else {
                    resource.close();
                }
            }
        }
    }

}
Compiled from "2a.java"
final class BogusResource implements java.lang.AutoCloseable {
  BogusResource();
    Code:
       0: aload_0       
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return        

  int read() throws java.lang.Exception;
    Code:
       0: iconst_0      
       1: ireturn       

  public void close() throws java.lang.Exception;
    Code:
       0: return        

  public static void f1() throws java.lang.Exception;
    Code:
       0: new           #2                  // class BogusResource
       3: dup           
       4: invokespecial #3                  // Method "<init>":()V
       7: astore_0      
       8: aconst_null   
       9: astore_1      
      10: aload_0       
      11: invokevirtual #4                  // Method read:()I
      14: pop           
      15: aload_0       
      16: ifnull        84
      19: aload_1       
      20: ifnull        39
      23: aload_0       
      24: invokevirtual #5                  // Method close:()V
      27: goto          84
      30: astore_2      
      31: aload_1       
      32: aload_2       
      33: invokevirtual #7                  // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
      36: goto          84
      39: aload_0       
      40: invokevirtual #5                  // Method close:()V
      43: goto          84
      46: astore_2      
      47: aload_2       
      48: astore_1      
      49: aload_2       
      50: athrow        
      51: astore_3      
      52: aload_0       
      53: ifnull        82
      56: aload_1       
      57: ifnull        78
      60: aload_0       
      61: invokevirtual #5                  // Method close:()V
      64: goto          82
      67: astore        4
      69: aload_1       
      70: aload         4
      72: invokevirtual #7                  // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
      75: goto          82
      78: aload_0       
      79: invokevirtual #5                  // Method close:()V
      82: aload_3       
      83: athrow        
      84: return        
    Exception table:
       from    to  target type
          23    27    30   Class java/lang/Throwable
          10    15    46   Class java/lang/Throwable
          10    15    51   any
          60    64    67   Class java/lang/Throwable
          46    52    51   any

  public static void f2() throws java.lang.Exception;
    Code:
       0: new           #2                  // class BogusResource
       3: dup           
       4: invokespecial #3                  // Method "<init>":()V
       7: astore_0      
       8: aconst_null   
       9: astore_1      
      10: aload_0       
      11: invokevirtual #4                  // Method read:()I
      14: pop           
      15: aload_0       
      16: ifnull        84
      19: aload_1       
      20: ifnull        39
      23: aload_0       
      24: invokevirtual #5                  // Method close:()V
      27: goto          84
      30: astore_2      
      31: aload_1       
      32: aload_2       
      33: invokevirtual #7                  // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
      36: goto          84
      39: aload_0       
      40: invokevirtual #5                  // Method close:()V
      43: goto          84
      46: astore_2      
      47: aload_2       
      48: astore_1      
      49: aload_2       
      50: athrow        
      51: astore_3      
      52: aload_0       
      53: ifnull        82
      56: aload_1       
      57: ifnull        78
      60: aload_0       
      61: invokevirtual #5                  // Method close:()V
      64: goto          82
      67: astore        4
      69: aload_1       
      70: aload         4
      72: invokevirtual #7                  // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
      75: goto          82
      78: aload_0       
      79: invokevirtual #5                  // Method close:()V
      82: aload_3       
      83: athrow        
      84: return        
    Exception table:
       from    to  target type
          23    27    30   Class java/lang/Exception
          10    15    46   Class java/lang/Exception
          10    15    51   any
          60    64    67   Class java/lang/Exception
          46    52    51   any
}

複数リソース版

final class BogusResource implements AutoCloseable {

    int read() throws Exception {
        return 0;
    }

    public void close() throws Exception {
    }

    public static void f1() throws Exception {
        try (BogusResource resource1 = new BogusResource();
             BogusResource resource2 = new BogusResource()) {
             resource1.read();
             resource2.read();
        }
    }

    public static void f2() throws Exception {
        BogusResource resource1 = new BogusResource();
        Throwable th1 = null;
        try {
            BogusResource resource2 = new BogusResource();
            Throwable th2 = null;
            try {
                resource1.read();
                resource2.read();
            } catch (Exception ex) {
                th2 = ex;
                throw ex;
            } finally {
                if (resource2 != null) {
                    if (th2 != null) {
                        try {
                            resource2.close();
                        } catch (Exception ex) {
                            th2.addSuppressed(ex);
                        }
                    } else {
                        resource2.close();
                    }
                }
            }
        } catch (Exception ex) {
            th1 = ex;
            throw ex;
        } finally {
            if (resource1 != null) {
                if (th1 != null) {
                    try {
                        resource1.close();
                    } catch (Exception ex) {
                        th1.addSuppressed(ex);
                    }
                } else {
                    resource1.close();
                }
            }
        }
    }

}
Compiled from "2b.java"
final class BogusResource implements java.lang.AutoCloseable {
  BogusResource();
    Code:
       0: aload_0       
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return        

  int read() throws java.lang.Exception;
    Code:
       0: iconst_0      
       1: ireturn       

  public void close() throws java.lang.Exception;
    Code:
       0: return        

  public static void f1() throws java.lang.Exception;
    Code:
       0: new           #2                  // class BogusResource
       3: dup           
       4: invokespecial #3                  // Method "<init>":()V
       7: astore_0      
       8: aconst_null   
       9: astore_1      
      10: new           #2                  // class BogusResource
      13: dup           
      14: invokespecial #3                  // Method "<init>":()V
      17: astore_2      
      18: aconst_null   
      19: astore_3      
      20: aload_0       
      21: invokevirtual #4                  // Method read:()I
      24: pop           
      25: aload_2       
      26: invokevirtual #4                  // Method read:()I
      29: pop           
      30: aload_2       
      31: ifnull        106
      34: aload_3       
      35: ifnull        56
      38: aload_2       
      39: invokevirtual #5                  // Method close:()V
      42: goto          106
      45: astore        4
      47: aload_3       
      48: aload         4
      50: invokevirtual #7                  // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
      53: goto          106
      56: aload_2       
      57: invokevirtual #5                  // Method close:()V
      60: goto          106
      63: astore        4
      65: aload         4
      67: astore_3      
      68: aload         4
      70: athrow        
      71: astore        5
      73: aload_2       
      74: ifnull        103
      77: aload_3       
      78: ifnull        99
      81: aload_2       
      82: invokevirtual #5                  // Method close:()V
      85: goto          103
      88: astore        6
      90: aload_3       
      91: aload         6
      93: invokevirtual #7                  // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
      96: goto          103
      99: aload_2       
     100: invokevirtual #5                  // Method close:()V
     103: aload         5
     105: athrow        
     106: aload_0       
     107: ifnull        177
     110: aload_1       
     111: ifnull        130
     114: aload_0       
     115: invokevirtual #5                  // Method close:()V
     118: goto          177
     121: astore_2      
     122: aload_1       
     123: aload_2       
     124: invokevirtual #7                  // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
     127: goto          177
     130: aload_0       
     131: invokevirtual #5                  // Method close:()V
     134: goto          177
     137: astore_2      
     138: aload_2       
     139: astore_1      
     140: aload_2       
     141: athrow        
     142: astore        7
     144: aload_0       
     145: ifnull        174
     148: aload_1       
     149: ifnull        170
     152: aload_0       
     153: invokevirtual #5                  // Method close:()V
     156: goto          174
     159: astore        8
     161: aload_1       
     162: aload         8
     164: invokevirtual #7                  // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
     167: goto          174
     170: aload_0       
     171: invokevirtual #5                  // Method close:()V
     174: aload         7
     176: athrow        
     177: return        
    Exception table:
       from    to  target type
          38    42    45   Class java/lang/Throwable
          20    30    63   Class java/lang/Throwable
          20    30    71   any
          81    85    88   Class java/lang/Throwable
          63    73    71   any
         114   118   121   Class java/lang/Throwable
          10   106   137   Class java/lang/Throwable
          10   106   142   any
         152   156   159   Class java/lang/Throwable
         137   144   142   any

  public static void f2() throws java.lang.Exception;
    Code:
       0: new           #2                  // class BogusResource
       3: dup           
       4: invokespecial #3                  // Method "<init>":()V
       7: astore_0      
       8: aconst_null   
       9: astore_1      
      10: new           #2                  // class BogusResource
      13: dup           
      14: invokespecial #3                  // Method "<init>":()V
      17: astore_2      
      18: aconst_null   
      19: astore_3      
      20: aload_0       
      21: invokevirtual #4                  // Method read:()I
      24: pop           
      25: aload_2       
      26: invokevirtual #4                  // Method read:()I
      29: pop           
      30: aload_2       
      31: ifnull        106
      34: aload_3       
      35: ifnull        56
      38: aload_2       
      39: invokevirtual #5                  // Method close:()V
      42: goto          106
      45: astore        4
      47: aload_3       
      48: aload         4
      50: invokevirtual #7                  // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
      53: goto          106
      56: aload_2       
      57: invokevirtual #5                  // Method close:()V
      60: goto          106
      63: astore        4
      65: aload         4
      67: astore_3      
      68: aload         4
      70: athrow        
      71: astore        5
      73: aload_2       
      74: ifnull        103
      77: aload_3       
      78: ifnull        99
      81: aload_2       
      82: invokevirtual #5                  // Method close:()V
      85: goto          103
      88: astore        6
      90: aload_3       
      91: aload         6
      93: invokevirtual #7                  // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
      96: goto          103
      99: aload_2       
     100: invokevirtual #5                  // Method close:()V
     103: aload         5
     105: athrow        
     106: aload_0       
     107: ifnull        177
     110: aload_1       
     111: ifnull        130
     114: aload_0       
     115: invokevirtual #5                  // Method close:()V
     118: goto          177
     121: astore_2      
     122: aload_1       
     123: aload_2       
     124: invokevirtual #7                  // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
     127: goto          177
     130: aload_0       
     131: invokevirtual #5                  // Method close:()V
     134: goto          177
     137: astore_2      
     138: aload_2       
     139: astore_1      
     140: aload_2       
     141: athrow        
     142: astore        7
     144: aload_0       
     145: ifnull        174
     148: aload_1       
     149: ifnull        170
     152: aload_0       
     153: invokevirtual #5                  // Method close:()V
     156: goto          174
     159: astore        8
     161: aload_1       
     162: aload         8
     164: invokevirtual #7                  // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
     167: goto          174
     170: aload_0       
     171: invokevirtual #5                  // Method close:()V
     174: aload         7
     176: athrow        
     177: return        
    Exception table:
       from    to  target type
          38    42    45   Class java/lang/Exception
          20    30    63   Class java/lang/Exception
          20    30    71   any
          81    85    88   Class java/lang/Exception
          63    73    71   any
         114   118   121   Class java/lang/Exception
          10   106   137   Class java/lang/Exception
          10   106   142   any
         152   156   159   Class java/lang/Exception
         137   144   142   any
}

*1:伝統的な命名規則。