Actually I (re)discovered that the three-argument form of Kernel#raise can be used to short-circuit backtrace generation:
raise SomeException, some_arg, nil
Here, some_arg will be pass to SomeException#initialize, and nil will be used for the backtrace. In JRuby 1.6.0.RC3 and higher, we will then not generate a backtrace, cutting out the performance hit you see here.
It obviously still requires people to adapt their code, but at least we have a way to opt-out of backtrace generation without adding anything to Ruby. Yay!
I'm going to mark this resolved/fixed, since we have done a little work, and there's no further improvement possible.
I did some cleanup in recent commits to refactor backtrace logic to try to reduce the perf hit as much as possible. On my machine, it takes about 1.5ms now to generate a full-length backtrace with default JVM settings, with the manipulation we do deferred until the backtrace is actually needed. I also made the modification that the three-arg Kernel#raise does not generate an internal backtrace anymore, so it will be a good workaround.
I also still plan to write up a blog post on all this, to educate Rubyists and give them some new tools to avoid the bad pattern of exceptions-as-flow-control, or to use the opt-out if they really must use exceptions this way.