1

I stumbled across an interesting error yesterday and have since fixed it, but it still was bothering me this morning, so I would like to see if anyone can shed some light on the issue.

The code in question:

final ResultSet rs = prepStatement.executeQuery();
try
{
   if (!rs.next())
   {
      throw new IllegalStateException("Expected non-empty result");
   }
   return rs.getInt(0 + 1);
}
finally
{
   rs.close();
}

Now for the part that doesn't make since. Every once in a while, the return statement will throw an exception indicating that getInt(int) has been called on a closed ResultSet. I verified that the prepared statement is not being closed anywhere in the code, and if the database was closing, I would see other errors as well. This leads me to believe that somehow, occasionally, the finally block is being executed before the return statement. The only thing I can think of is that the hotspot compiler doesn't always get this right. I'm using the Oracle JVM listed below.

java version "1.7.0_45"
Java(TM) SE Runtime Environment (build 1.7.0_45-b18)
Java HotSpot(TM) 64-Bit Server VM (build 24.45-b08, mixed mode)

I feel like I should mention that I have seen the host of other questions about this ordering, but they all seem to indicate that it is set in stone, where I seem to be witnessing something different.

Try-catch-finally-return clarification
https://stackoverflow.com/questions/20164755/the-order-of-invoking-finally-block
Does finally always execute in Java?

Community
  • 1
  • 1
Alex N.
  • 654
  • 8
  • 21
  • 2
    _that somehow, occasionally, the finally block is being executed_ No, it **always** gets executed. `rs.getInt()` is executed and is stored as a return value, then `rs.close()` is executed. Then the value is returned. – Sotirios Delimanolis Dec 04 '13 at 16:36
  • 2
    Agreed, but what I actually said was, *This leads me to believe that somehow, occasionally, the finally block is being executed before the return statement*. And that's what I would expect, but that's not what I see happening, and I'm curious why? – Alex N. Dec 04 '13 at 16:38
  • 3
    Can you post the exception stack trace and the name of the method your `finally` block appears in? – Sotirios Delimanolis Dec 04 '13 at 16:41
  • Maybe. I'll stick the broken version back in while I work on other stuff today, and see if I can get the error to rear its head again. If it does, I'll post the trace. – Alex N. Dec 04 '13 at 16:46
  • How about multiple threads maybe? – Jan Zyka Dec 04 '13 at 16:47
  • Nope. Just one thread. – Alex N. Dec 04 '13 at 16:50

1 Answers1

1

I wrote and compiled the following class

public class Examples {
    public int answer(PreparedStatement prepStatement) throws SQLException {
        final ResultSet rs = prepStatement.executeQuery();
        try {
            if (!rs.next()) {
                throw new IllegalStateException("Expected non-empty result");
            }
            return rs.getInt(1);
        } finally {
            rs.close();
        }
    }
}

with the following commands

[s_delima@ml-l-sotiriosd Downloads]$ java -version
java version "1.7.0_45"
Java(TM) SE Runtime Environment (build 1.7.0_45-b18)
Java HotSpot(TM) 64-Bit Server VM (build 24.45-b08, mixed mode)
[s_delima@ml-l-sotiriosd Downloads]$ javac Examples.java 
[s_delima@ml-l-sotiriosd Downloads]$ /usr/java/latest/bin/javap -c Examples
Compiled from "Examples.java"
public class Examples {
  public Examples();
    Code:
       0: aload_0       
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return        

  public int answer(java.sql.PreparedStatement) throws java.sql.SQLException;
    Code:
       0: aload_1       
       1: invokeinterface #2,  1            // InterfaceMethod java/sql/PreparedStatement.executeQuery:()Ljava/sql/ResultSet;
       6: astore_2      
       7: aload_2       
       8: invokeinterface #3,  1            // InterfaceMethod java/sql/ResultSet.next:()Z
      13: ifne          26
      16: new           #4                  // class java/lang/IllegalStateException
      19: dup           
      20: ldc           #5                  // String Expected non-empty result
      22: invokespecial #6                  // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V
      25: athrow        
      26: aload_2       
      27: iconst_1      
      28: invokeinterface #7,  2            // InterfaceMethod java/sql/ResultSet.getInt:(I)I
      33: istore_3      
      34: aload_2       
      35: invokeinterface #8,  1            // InterfaceMethod java/sql/ResultSet.close:()V
      40: iload_3       
      41: ireturn       
      42: astore        4
      44: aload_2       
      45: invokeinterface #8,  1            // InterfaceMethod java/sql/ResultSet.close:()V
      50: aload         4
      52: athrow        
    Exception table:
       from    to  target type
           7    34    42   any
          42    44    42   any
}

If you follow along with byte code instructions, you will see that at 28, the rs.getInt(1) is invoked and its value is stored at 33. The rs.close() is invoked at 35. The stored value is retrieved at 40 and returned at 41.

What you are experiencing must come from some other point in your code.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
  • Well damn. Same version and everything. I would say that's some pretty good evidence that there is some funny business here. I'm gonna wait a bit and see if I can get the error to show up. – Alex N. Dec 04 '13 at 17:48
  • @Alex Please do. I'm curious, too. – Sotirios Delimanolis Dec 04 '13 at 17:53
  • More than a month later, we finally solved this problem. As it turns out the bug had to do with threads timing out inside the particular database driver we were using. I'm marking this correct because when the error showed up, this pushed me towards searching in the correct place. – Alex N. Jan 24 '14 at 13:50
  • @AlexN. What driver? So I can stay away from it :) – Sotirios Delimanolis Jan 24 '14 at 14:32