13

Using Java 7u5, with the try-with-resources construct, the following code appears to leak jdbc connections:

try (Connection connection = ..; PreparedStatement stmt = ..) {
    stmt.setString(..);
    return stmt.executeUpdate() > 0;
}

The next piece of code works as expected and intended:

int ret = 0;

try (Connection connection = ..; PreparedStatement stmt = ..) {
    stmt.setString(..);
    ret = stmt.executeUpdate();
}

return ret > 0;

It seems that in the first case, the Connection.close() method is not being invoked.

I am using the latest mysql connector. This is unexpected behavior, correct?

Test

The following test will NOT print CLOSED:

public class Test implements AutoCloseable {

public static void main(String[] args) throws Exception {
    System.out.println(doTest());
}

private static boolean doTest() throws Exception {
    try (Test test = new Test()) {
        return test.execute() > 0;
    }

}

private int execute() {
    return 1;
}

@Override
public void close() throws Exception {
    System.out.println("CLOSED");
}
}

Strangely, if execute() is modified to return 0; then CLOSED WILL be printed.

javap -p -c Test.class output

    Compiled from "Test.java"
public class Test implements java.lang.AutoCloseable {
  public Test();
    Code:
       0: aload_0
       1: invokespecial #10                 // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]) throws java.lang.Exception;
    Code:
       0: getstatic     #21                 // Field java/lang/System.out:Ljava/io/PrintStream;
       3: invokestatic  #27                 // Method doTest:()Z
       6: invokevirtual #31                 // Method java/io/PrintStream.println:(Z)V
       9: return

  private static boolean doTest() throws java.lang.Exception;
    Code:
       0: aconst_null
       1: astore_0
       2: aconst_null
       3: astore_1
       4: new           #1                  // class Test
       7: dup
       8: invokespecial #39                 // Method "<init>":()V
      11: astore_2
      12: aload_2
      13: invokespecial #40                 // Method execute:()I
      16: ifle          21
      19: iconst_1
      20: ireturn
      21: iconst_0
      22: aload_2
      23: ifnull        30
      26: aload_2
      27: invokevirtual #44                 // Method close:()V
      30: ireturn
      31: astore_0
      32: aload_2
      33: ifnull        40
      36: aload_2
      37: invokevirtual #44                 // Method close:()V
      40: aload_0
      41: athrow
      42: astore_1
      43: aload_0
      44: ifnonnull     52
      47: aload_1
      48: astore_0
      49: goto          62
      52: aload_0
      53: aload_1
      54: if_acmpeq     62
      57: aload_0
      58: aload_1
      59: invokevirtual #47                 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
      62: aload_0
      63: athrow
    Exception table:
       from    to  target type
          12    22    31   any
          30    31    31   any
           4    42    42   any

  private int execute();
    Code:
       0: iconst_1
       1: ireturn

  public void close() throws java.lang.Exception;
    Code:
       0: getstatic     #21                 // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #55                 // String CLOSED
       5: invokevirtual #57                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return
}
beefyhalo
  • 1,691
  • 2
  • 21
  • 33
  • Is it possible to see what code this compiles to? Decompiler or something? – Thilo Jul 16 '12 at 01:19
  • I'll assume you're seeing dangling connections in MySQL? Have you looked at this thread from SO? http://stackoverflow.com/questions/5765990/java-mysql-jdbc-memory-leak – Zeleres Jul 16 '12 at 01:40
  • 1
    Thx @Zeleres for the comment. No connections are being shared across threads, so the lifetime of the connection you see here should last only inside the scope it's declared in. – beefyhalo Jul 16 '12 at 01:45
  • 1
    I don't have Java 7 installed on the computer that I'm on right now, so I can't try it, but your 2 code samples look like they should behave exactly the same way. Here's something to try - replace the Connection and PreparedStatement with a class of your own that implements Closeable. In your custom class, log the invocation of the close() method. Try it in both code samples, and see what happens. If close() gets called in both cases, then the problem is elsewhere. – GreyBeardedGeek Jul 16 '12 at 01:53
  • @GreyBeardedGeek I updated the question to include the fact that `Connection.close()` does not get invoked in the top case – beefyhalo Jul 16 '12 at 02:08
  • `try`-with-resources statements, according to [JLS §14.20.3.1](http://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.20.3.1), are translated into `try-catch-finally` statements. `close` is guaranteed to be called on every declared resource as long as it is non-`null`. What is the output of calling `javap -c` on your compiled class file? – Jeffrey Jul 16 '12 at 02:57
  • Copying the `Test` class into Eclipse and running it prints `CLOSED` (and `true`). – Joachim Sauer Jul 16 '12 at 14:39
  • The code of the `doTest()` method is missing from the javap output. – Philipp Wendler Jul 16 '12 at 14:40
  • @JoachimSauer what version of java are you using? I only get the output `true` – beefyhalo Jul 16 '12 at 14:44
  • `1.7.0_02` 64-Bit Server on Windows (both with the Eclipse compiler and `javac`). – Joachim Sauer Jul 16 '12 at 14:46
  • @PhilippWendler This is the full output from `javap -c Test.class` – beefyhalo Jul 16 '12 at 14:46
  • 1
    @Beefyhalo: `private` methods/fields are not usually shown by `javap`. You need to add `-p` to get the (large-ish) output for `doTest`. – Joachim Sauer Jul 16 '12 at 14:46
  • @JoachimSauer thx, question updated – beefyhalo Jul 16 '12 at 14:49
  • @JoachimSauer I tried to compile/run this code with `1.7.0_02 64bit`, and I get my original (incorrect) output – beefyhalo Jul 16 '12 at 14:57
  • 1
    @Beefyhalo: are you using `javac` or some other compiler (Eclipse?). – Joachim Sauer Jul 16 '12 at 14:59
  • @JoachimSauer I was using eclipse in fact. It seems to work fine with javac! – beefyhalo Jul 16 '12 at 15:18
  • @Beefyhalo: then the question is: which version of Eclipse? Because my 4.2 install works just fine as well. – Joachim Sauer Jul 16 '12 at 15:19
  • @JoachimSauer If I'm not mistaken, I am using Eclipse Indigo (1.4.1?) – beefyhalo Jul 16 '12 at 15:24
  • @JoachimSauer After upgrading Eclipse to Juno, all works well. Thx a bunch for your help! – beefyhalo Jul 16 '12 at 15:36

5 Answers5

3

After upgrading to the latest version of eclipse (Juno), this strange behavior is no longer occurring.

It also works fine using the command line to compile and run.

I suspect Eclipse Indigo was using an old javac to compile, and not complaining about any compliance violation.

beefyhalo
  • 1,691
  • 2
  • 21
  • 33
2

I've encountered the very same issue using JDK 1.7.0_17. After careful elimination, it turned out it my IntelliJ was using an AspectJ compiler. Once I compiled the class explicitly with the JDK's javac it worked as expected.

My colleague has filed a bug report to the AspectJ people. They have scheduled a fix for version 1.7.3

bowmore
  • 10,842
  • 1
  • 35
  • 43
1

It is a java 7u5 bug; go register a bug. Java 7u4 worked.

return test.execute() > 0;

gives wrong code for > 0:

  13: invokespecial #40                 // Method execute:()I
  16: ifle          21
  19: iconst_1
  20: ireturn
Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
0

EDIT

  1. In Eclipse Juno it works very well. I don't think it is a java 7 bug. It is not working when run from Eclipse Indigo.

  2. It also works from Command Line.

Previous Answer

In following cases I run your program and it worked, I am checking your case

case 1 :

public class Test implements AutoCloseable {

    public int execute() {
        return 1;
    }

    @Override
    public void close() throws Exception {
        System.out.println("CLOSED");
    }
}



public class Test1 {

    public static void main(String[] args) throws Exception {

        try (Test test = new Test()) {
            System.out.println(test.execute() > 0);
        }

    }

}

Output :

true
CLOSED

case 2 :

public class Test implements AutoCloseable {

    public static void main(String[] args) throws Exception {
        System.out.println(doTest());
    }

    private static boolean doTest() throws Exception {
        try (Test test = new Test()) {
            throw new ArrayIndexOutOfBoundsException("exc"); // just for testing
            //return test.execute() > 0;
        }

    }

    private int execute() {
        return 1;
    }

    @Override
    public void close() throws Exception {
        System.out.println("CLOSED");
    }
}

Output :

CLOSED
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: exc
    at com.aquent.rambo.auth.Test.doTest(Test.java:11)
    at com.aquent.rambo.auth.Test.main(Test.java:6)

case 3 : Here this is wierd, and it works

public class Test implements AutoCloseable {

    public static void main(String[] args) throws Exception {
        System.out.println(doTest());
    }

    private static boolean doTest() throws Exception {
        try (Test test = new Test()) {
            boolean result = test.execute() > 0; // Change : result variable declared
            return result;
        }

    }

    private int execute() {
        return 1;
    }

    @Override
    public void close() throws Exception {
        System.out.println("CLOSED");
    }
}

Output :

CLOSED
true
Nandkumar Tekale
  • 16,024
  • 8
  • 58
  • 85
-3

Have you tried to close the statements and/or the connections after you're done.

Also make sure to do this in a finally block:

Stephane Grenier
  • 15,527
  • 38
  • 117
  • 192