Excepción sin seguimiento de stack en Java

Esta es probablemente una pregunta muy ingenua.

Solía ​​creer que un Throwable en Java siempre contiene el seguimiento de la stack. ¿Es correcto? Ahora parece que capturo exceptions sin el seguimiento de la stack. ¿Tiene sentido? ¿Es posible atrapar una excepción sin el seguimiento de la stack?

Es posible capturar un objeto Throwable en Java sin un rastro de stack:

 Throwable(String message, Throwable cause, boolean enableSuppression,boolean writableStackTrace) 

Construye un nuevo throwable con el mensaje de detalle especificado, causa, supresión activada o desactivada, y el registro de stack editable habilitado o deshabilitado.

http://docs.oracle.com/javase/7/docs/api/java/lang/Throwable.html

Para Java 6:

Como Java 6 no tiene el constructor Throwable(String message, Throwable cause, boolean enableSuppression,boolean writableStackTrace) , podemos suprimir el llenado de stacktrace usando la técnica siguiente (tomado de Scala, se llegó a saber de ¿Qué tan lentas son las excepciones de Java? )

 class NoStackTraceRuntimeException extends RuntimeException { @Override public synchronized Throwable fillInStackTrace() { return this; } } 

El uso es el mismo: throw new NoStackTraceRuntimeException () , o sus subtipos.

También podemos hacer lo mismo al extender Throwable :

 class NoStackTraceThrowable extends Throwable { @Override public synchronized Throwable fillInStackTrace() { return this; } } 

Pero, una pequeña pega es que ya no puede catch estas excepciones usando Exception ya que este no es un subtipo de Exception , sino que debe capturar NoStackTraceThrowable o sus subtipos.

Actualización : para obtener estadísticas interesantes sobre el rendimiento en diferentes usos, consulte esta pregunta

Para Java 7+, aquí hay un ejemplo de una excepción donde el rastreo de la stack puede ser suprimido opcionalmente.

 public class SuppressableStacktraceException extends Exception { private boolean suppressStacktrace = false; public SuppressableStacktraceException(String message, boolean suppressStacktrace) { super(message, null, suppressStacktrace, !suppressStacktrace); this.suppressStacktrace = suppressStacktrace; } @Override public String toString() { if (suppressStacktrace) { return getLocalizedMessage(); } else { return super.toString(); } } } 

Esto se puede demostrar con:

 try { throw new SuppressableStacktraceException("Not suppressed", false); } catch (SuppressableStacktraceException e) { e.printStackTrace(); } try { throw new SuppressableStacktraceException("Suppressed", true); } catch (SuppressableStacktraceException e) { e.printStackTrace(); } 

Esto se basa en la MLContextException de Apache SystemML , cuyo código está disponible en GitHub en https://github.com/apache/systemml .

La forma más fácil de suprimir el stacktrace en cualquier excepción es

 throwable.setStackTrace(new StackTraceElement[0]); 

Si la excepción tiene una causa, es posible que deba hacer lo mismo de forma recursiva.

Esto también reduce la costosa creación de la traza de stack, tanto como sea posible

La stacktrace para un throwable se inicializa en

 Throwable#fillInStackTrace() 

, que es llamado por cualquier constructor y, por lo tanto, no se puede evitar. Cuando en realidad se utiliza stacktrace, un StackTraceElement [] se construye de forma

 Throwable#getOurStackTrace() 

que solo ocurre si el campo Throwable.stackTrace no estaba ya configurado.

Establecer el stacktrace en cualquier valor no nulo, evita la construcción de StackTraceElement [] en Throwable # getOurStackTrace () y reduce la penalización de rendimiento tanto como sea posible.