That said, the reason you can't effectively catch an Error is that it
represents a situation so fubared that there is no effective recovery
available. Still, you can declare a catch block to attempt to minimize the
damage.
Curiously, I'd say that StackOverflowError, at minimum, may be an
exception to this rule. I can imagine e.g.
public int myMethod (int arg) {
if (isBaseCase(arg)) return 0;
try {
return recursiveMyMethod(arg);
} catch (StackOverflowError e) {
throw new IllegalArgumentException("Argument " + arg + " to
myMethod is too big.");
}
}
The stack unwind "undoes" the damage of a stack overflow, since the
stack ends up as stubby as it was before the recursive call. Of course
the caller might still just upchuck on the IllegalArgumentException.
It's not hard to imagine a less efficient nonrecursive version of
myMethod being called, though, or some other such recovery depending
on the situation, or the result of the IAE being a particular user
operation failing without the whole app bombing.
In multithreaded parallelized code, another way to "recover" from even
OOME is to let it kill the thread it arises within. Another thread
goes to allocate something and this forces a GC, which reclaims all
the resources the now-dead thread held. Memory pressure thus leads to
scaled-back parallelization. With a control thread that holds the job
queues and has some ability to detect worker thread disappearance,
jobs can be reissued. Performance degrades instead of the task
bombing. A factory can attempt to recreate the missing worker threads
after some time has elapsed. A "fat job" could cause half the threads
to die but eventually be done with and the system would bounce back up
to its usual performance after a while.
Ultimately, how recoverable or irrecoverable any of these are is
dependent on the requirements and details of what's being implemented.
Certainly typical applications with user documents need to catch all
of them, including and especially OOME, and do a panic-save of the
user's session and unsaved data to where the next session will attempt
to recover, at minimum. (Not over the original files in case of data
corruption or other inconsistent transitional states caused by the
interrupting throwable!) To be able to do this, the exception handler
can trigger a graceful but non-memory-allocating shutdown that tells
threads to quit, destroys displayed UI, and drops references to most
objects other than those representing unsaved data, then call
System.gc() (though this should probably not be necessary), then
create some streams and save the unsaved data to the recovery file,
then pop up an apologetic error message, before shutting down. The
memory freed by first disposing of the previously-visible UI objects
alone is probably sufficient to do this successfully during exit.