30

I am trying to convince myself that actions taken in the finally clause happen before the function return (in the memory consistency sense). From the JVM specification, it is clear that within a thread, program order is supposed to drive the happens before relationship -- if a happens b in the program order then a happens before b.

However, I have not seen anything explicitly stating that finally happens before return, so does it? Or, is there some way that the compiler could reorder the finally clause since it is simply logging.

Motivating example: I have one thread fetching objects out of a database and am putting them into an ArrayBlockingQueue, and another thread is taking them out. I have some try- finally blocks for event timing, and I am seeing after affects of the return before the log statement

Thread 1:

public Batch fetch() {
    try {
        log("fetch()+");
        return queryDatabase();
    }
    finally {
        log("fetch()-");
    }
     ...
    workQueue.put(fetch());

Thread 2:

log("take()+");
Batch b = workQueue.take();
log("take()-");

To my great surprise this prints out in an unexpected order. While, yes, logging statements in different threads can appear out of order, there is a time difference of at least 20 ms.

124 ms : take()+
224 ms : fetch()+
244 ms : take()-
254 ms : fetch()-

Note this is not exactly the same question as does finally trump return. I'm not asking what will be returned, but instead about memory consistency and execution order.

Community
  • 1
  • 1
Justin
  • 4,437
  • 6
  • 32
  • 52
  • Someone made a really interesting point about function returns not being instructions that could be re-ordered, then they deleted it. If you put it back, i'll vote for it as helpful -- it seemed like a very good line of pursuit although with inlining + jit reorder i'm not sure that is true. – Justin Apr 18 '11 at 21:16
  • I attribute my test results to bad memory, I had a 1 bit error in the last bit for several addresses (e.g. FFFF gets written as FFFE). Who can be sure what this did to the JIT. – Justin Apr 20 '11 at 19:09

4 Answers4

20

The call to queryDatabase() happens first. Then the finally block. Then control leaves the function (that's the return).

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
18

@David Heffernan has the correct answer. The JLS specification talks about the behavior of the return statement(including how it interacts with finally blocks) in section 14.17. Copying from there (emphasis mine):

A return statement with an Expression attempts to transfer control to the invoker of the method that contains it; the value of the Expression becomes the value of the method invocation. More precisely, execution of such a return statement first evaluates the Expression. If the evaluation of the Expression completes abruptly for some reason, then the return statement completes abruptly for that reason. If evaluation of the Expression completes normally, producing a value V, then the return statement completes abruptly, the reason being a return with value V. If the expression is of type float and is not FP-strict (§15.4), then the value may be an element of either the float value set or the float-extended-exponent value set (§4.2.3). If the expression is of type double and is not FP-strict, then the value may be an element of either the double value set or the double-extended-exponent value set.

It can be seen, then, that a return statement always completes abruptly.

The preceding descriptions say "attempts to transfer control" rather than just "transfers control" because if there are any try statements (§14.20) within the method or constructor whose try blocks contain the return statement, then any finally clauses of those try statements will be executed, in order, innermost to outermost, before control is transferred to the invoker of the method or constructor. Abrupt completion of a finally clause can disrupt the transfer of control initiated by a return statement.

gcooney
  • 1,689
  • 10
  • 14
  • This passage is also referenced in the 'does finally trump return' SO question. If I understand your emphasis, the finally _should_ come before the return in the program order since, per the last passage, "before control is transferred to the invoker of the method" – Justin Apr 18 '11 at 21:50
  • I really wish I understood how it came to be that the log showed it the other way, my JVM sometimes crashes inexplicably so ... – Justin Apr 18 '11 at 21:53
  • 2
    @Justin: The log might have some delay built in. I think you only synchronized on the queue, not the log. – Paŭlo Ebermann Apr 18 '11 at 21:57
  • It must be that, but most of the time, statements which could get logged appear in the same millisecond -- not 20 ms apart. I know its not scientific, but it seems convincing in an anecdotal sense. – Justin Apr 18 '11 at 22:12
  • I do not believe there is a happens-before edge between finally and the return. The above paragraph does not mention anything about inter-thread visibility. The JLS is pretty clear about what provides happens-before: http://java.sun.com/docs/books/jls/third_edition/html/memory.html#17.4.5 – sjlee Apr 18 '11 at 22:50
  • 1
    @sjlee The finally and the return occur on the same thread. Because they are on the same thread, the happens-before relationship is covered by the first bullet point in the section you link. – gcooney Apr 18 '11 at 23:10
  • 1
    The "happens-before" language is bit of a poor choice then, because in the context of JLS, happens-before is always used in the context of inter-thread visibility, and not to describe a intra-thread sequence. – sjlee Apr 19 '11 at 05:14
  • True, but I thought I was seeing the effect of the return in another thread before the finally (possibly as a result of inlineing). I don't really know how deep the JIT can go. – Justin Apr 19 '11 at 05:52
  • @sjlee I agree that it's really execution order being asked about here. Tthe finally will definitely execute before the queryDatabase result is placed on the workQueue. – gcooney Apr 19 '11 at 13:51
  • @Justin The JIT should not do anything that modifies the behavior of the program. If it does it's a bug. But I think it's much more likely the mis-order of the outputs has to do with your logging somehow. One thing you could try is to create a second ArrayBlockingQueue and implement your log function to add log statements to the queue. At the end of thread2, pull all the log statements out of the queue and print them one by one. This should guarantee they print in the order they occured. I wouldn't recommend this long term but it could be useful as a test. – gcooney Apr 19 '11 at 13:55
3

The finally clause shall execute no matter what is the result or behaviour of the try block, so the finally gets executed before the return.

Will Marcouiller
  • 23,773
  • 22
  • 96
  • 162
2

If you are using only one thread, you should see "take+, fetch+, fetch-, take-". In your example, it's multi-threaded, so you are not sure what happens first.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Marcos Vasconcelos
  • 18,136
  • 30
  • 106
  • 167