
Why Java exceptions are slow (and Common Lisp conditions aren't) - mdasen
http://groups.google.com/group/comp.lang.lisp/msg/6157c055503e8ba8
======
barrkel
FWIW, structured exception handling (SEH) on Windows (and used by .NET etc.)
does not use the approach described herein.

SEH scans the stack twice. The first time, it calls handlers in reverse order
of registration (handlers are registered on function entry) asking them if
they handle this particular exception. Any handler can return a condition code
to tell the OS to essentially ignore the exception and continue with the next
instruction (a bit like VB 'On Error Resume Next'); perhaps the handler
repaired the error condition.

It's only after it has found a handler that indicates that it can handle the
given exception that the OS goes back and actually unwinds the stack. That
process involves calling all the handlers a second time, only this time
passing them a flag indicating that unwinding is actually taking place. That's
when 'finally' blocks get run. The OS keeps on going until it reaches the
handler that indicated it could handle the exception.

That's how things work in Win32. The situation in Win64 is slightly different,
as rather than FS:0 containing a pointer to the head of the exception chain,
program-counter-based lookup tables are used instead. However, the two-phase
exception dispatch is still used.

------
axod
An exception, should be - by definition - exceptional. Why would it matter
that much if it's slow? I think that's premature optimization...

There are a lot more important things to make sure are fast.

If most of your CPU time is spent throwing Exceptions, something is probably
seriously wrong.

~~~
mechanical_fish
The title of this HN submission is subtly misleading. The original post
discusses the fact that Java's exceptions are slow, but merely as a symptom of
the main problem: Java mixes up the issues of (a) finding some code that knows
how to handle an exceptional condition and (b) backing up the call stack until
it can find some code to continue executing. In Java, (b) is unavoidable, so
the context of every exception is destroyed before the exception handler can
examine it.

As noted elsewhere in this discussion, this means that Java exceptions aren't
resumable [1] and that a Java exception can't examine all the variables that
were alive when the exception was thrown and take action (like: logging) based
on the values of those variables [2]. Plus there may be memory-usage
implications. All of which are dimensions of suck that are independent of the
speed issue.

\---

[1] Not that I really know what this means, having never learned a language
where this was possible.

[2] This, on the other hand, would be win that I could understand.

~~~
snprbob86
Couldn't [1] be implemented quite simply by some sort of exception handler
manager thinggie?

Exception.OnThrown += LogThrownException;

Where this callback has some signature which can examine the stack, locals,
and potentially resume?

~~~
dfox
By doing this you will get something highly similar to CL's conditions. In CL,
there is mechanism similar to Java exceptions (throw/catch/unwind-protect)
that is - among other things - used by condition system to actually unwind
stack when it determines that is necessary. So you can probably abuse Java
exceptions to implement condition system or - more generally - escape
continuations.

Problem is that such mechanism has to be used consistently everywhere to be
useful. And when you find yourself rewriting standard library, you could well
use CL instead of Java.

------
gcv
Kent Pitman, a member of the X3J13 Common Lisp standards committee, wrote a
terrific paper ([http://www.nhplace.com/kent/Papers/Condition-
Handling-2001.h...](http://www.nhplace.com/kent/Papers/Condition-
Handling-2001.html)) on the subject of various condition handling systems,
which includes examples of various error-handling systems.

~~~
russell
Also interesting because it mentions PL/I as an antecedent to the Lisp
condition handling. It says that PL/I had downward lexical scoping. I can't
remember the details (I haven't looked at PL/I for over 20 years), but I
believe that it means that the PL/I exception handler had access to the whole
call stack, so you could make meaningful stack dumps from the handler,
including variables. By today's standards the whole thing was quite clunky. A
RETURN from the handler would resume execution. A GOTO to a label in the
procedure enclosing the handler would unwind the call stack.

------
rzwitserloot
I don't know if the JVM does this right now, but you CAN detect useless stack
building and simply not do it. In other words, the CPU cycle penalty of java's
(and most other language's) exception mechanism can be virtually eliminated.
The other benefits (resuming, for example) are a different issue.

The issue that I think the lisper of the OP is talking about is something like
this:

try { something(); } catch ( SomeException e ) { //don't ever even look at e's
stack, and do something else. }

Assuming the exception occurs, then to be safe the VM has to build the entire
stack, which involves unwinding a lot of JIT work if you're unlucky, even
though at the end it wasn't actually needed. However, assuming it matters that
this is slow, which pretty much means that this is in a tight loop someplace,
then a JIT compiler can detect that this situation always occurs, and simply
avoid creating the stack.

There are still very interesting applications, such as logging and resuming,
for not unwinding down the stack before giving the handlers in the chain an
opportunity, but the CPU argument seems void to me.

------
tlrobinson
Like just about everything in programming it seem to be a tradeoff.

In Java the try/catches are (a) implicitly "registered" on the call stack, and
when an exception is thrown (b) _then_ the runtime has to do a little analysis
to determine what to do.

In Lisp, the handlers are (a) explicitly registered up front and when an
"exception" is thrown (b) it just uses the most recently registered handler.

(a) is a much more common task than (b), so that's where you should optimize.
But I don't know enough enough about Lisp (or Java) to know how these are
implemented. If it's just pushing/popping on a stack then maybe it's just as
fast.

------
klahnako
Sorry, I am not convinced.

Java's exception handling may be slower, but exceptions should be rare: In the
few cases exceptions are not rare, return an object instead.

The issue of loosing some variables is misleading. First, the heap is not
rolled back, so many of the side effects are still available for inspection.
Second, how often do you want one method to handle another method's variables,
and if you do, is either method maintainable? Finally, if frame variables are
important, then you can put them in the exception.

If you want to complain about loosing context, try PL-SQL: "Raising" an
exception rolls back all evidence of there being a problem.

------
st3fan
Talking about things being 'godawful slow' is lame without showing context and
timings.

~~~
st3fan
_Caught 1000000 exceptions in 859 millis_

See <http://kip.sateh.com/ExceptionsAreSlow.txt>

------
stcredzero
So why aren't Java exceptions resumable?

~~~
smanek
Because by the time a java exception is caught, the stack has already been
unwound too far (because java must do so to find the nearest exception
handler).

Lisp, basically, keeps track of exception handlers in a table seperate from
the runtime stack. Hence, it doesn't need to unwind the stack to find an
exception handler - it can just look it up without destroying the existing
stack.

~~~
dhimes
And I find this to be a complete pain in the ass. Seriously, you can't even
print out variables to see what went wrong when you are building/debugging.
They're no longer in scope.

~~~
gruseom
What do you mean by "this" - Java's unwinding of the stack? Because in Lisp
you just visit whatever stack frame you want and see everything that's in
scope.

The real problem with Lisp environments (at least the open-source ones) is the
lack of acceptable modern debuggers.

~~~
gnaritas
Try Smalltalk, it has both a modern IDE/Debugger with automatic refactoring
support and resumable/restartable exceptions/notifications.

~~~
gruseom
Smalltalk does have some advantages over Lisp, including the ones you mention.
But lack of a good debugger isn't enough to make me switch. I like Lisp a lot.
Besides, switching to Smalltalk would be more work than writing (modernizing,
really) a Lisp debugger.

~~~
stcredzero
Just steal ideas from the Smalltalk debuggers. You can see their source. If
you make a copy of the debugger, you can probably even debug and step through
the copy debugger in the debugger.

