2010-02-27 9 views
14

En un sistema de registro, cada salida del registro es realizado por una clase de ayuda con un método como este¿Qué tan caro es Thread.getStackTrace()?

public void debug(String message) { 
    Logger logger = Logger.getLogger(getCallingClass()); 
    logger.debug(message); 
} 
... 
public Class getCallingClass() { 
/* 
Calls Thread.getStackTrace() and back traces until the class on the stack trace 
!= this.getClass(). 
*/ 
    return classFound; 
} 

¿Qué tan caro es esta habilitada y puede que tenga éxitos significativas en el rendimiento?

Respuesta

8

sí, hay algo de sobrecarga a este llamado, pero con toda probabilidad, usted va a hacer algo como esto:

public static boolean DEBUG_ON = true; //change this before your production build 

entonces,

public void debug(String message){ 
    if(DEBUG_ON){ 
    //stack code here 
    } 

} 

Qué le causará a no tomar el golpe en tu código real.

Incluso en ese caso, por excepciones, arrojará toda una excepción de seguimiento de la pila en su compilación de producción.

Tenga en cuenta que si está utilizando un subsistema de registro decente, probablemente ya hagan algo en función del nivel de registro (en nuestro sistema de registro, dependiendo del nivel, la depuración() es básicamente no operativa). Log4j y otros tienen diferentes formas de manejar esto.

Por último, yo diría: no te preocupes hasta que se demuestre que es un problema de rendimiento real. La optimización prematura es la raíz de todos los males :)

+1

En lugar de usar un indicador booleano, esta condición podría utilizarse como registrador.isDebugEnabled() – Inv3r53

+1

una mejor manera es usar directivas de preprocesamiento. – Orentet

+0

Hay beneficios para ambos. con el booleano final estático, el compilador simplemente eliminará cualquier cosa dentro de la sentencia if(), por lo que es como una directiva de preprocesador;) – Kylar

4

Por lo que recuerdo, hay un impacto en el uso de Thread.getStackTrace(), especialmente con pilas grandes (como cuando se usan en situaciones del lado del servidor o J2EE). Puede probar Throwable.getStackTrace() para un mejor rendimiento.

En cualquier caso, llamar a esas funciones regularmente (en lugar de hacerlo en una situación de excepción) afectará su aplicación.

+0

Dudo si el nuevo 'Throwable(). GetStackTrace()' va a algo más que llamar 'Thread.getStackTrace()' debajo del capó. –

+6

@SoftwareMonkey en realidad lo hace: Throwable.fillInStackTrace() puede tomar ventaja de saber que está examinando la pila para el mismo hilo que llama al método, mientras que Thread.getStackTrace() tiene que ser seguro para subprocesos y es mucho más costoso. Consulte http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6375302 –

+0

@David: es bueno saberlo, gracias. –

6

Parece que obtener el hilo actual (y su ID asociado) no es costoso, pero obtener el hilo actual y su rastro de pila es. El nuevo patrón throwable(). GetStackTrace() parece ser mucho más rápido que el patrón de seguimiento de pila del subproceso.

Además, tenga en cuenta que este punto de referencia casi no tiene profundidad de pila ya que es solo un método principal, por lo que en un entorno de servidor, esta penalización será mucho más pesada.

resultados Objetivo de referencia:

bucle simple tomó 2 ms

Obtención de hilo actual tomó 10 ms

Conseguir seguimiento de la pila tomó 29564 ms

Obtención throwable seguimiento de la pila se llevó a 19910 ms

Código:

int trials = 10_000_000; 

    long start = System.currentTimeMillis(); 

    long a = 1; 
    for (int i = 0; i < trials; i += 1) { 
     a += 1; 
    } 

    long duration = System.currentTimeMillis() - start; 
    System.out.println("Simple loop took " + duration + " ms"); 

    start = System.currentTimeMillis(); 

    a = 1; 
    for (int i = 0; i < trials; i += 1) { 
     a += 1; 
     Thread.currentThread().getId(); 
    } 

    duration = System.currentTimeMillis() - start; 
    System.out.println("Getting current thread took " + duration + " ms"); 

    start = System.currentTimeMillis(); 

    a = 1; 
    for (int i = 0; i < trials; i += 1) { 
     a += 1; 
     Thread.currentThread().getStackTrace(); 
    } 

    duration = System.currentTimeMillis() - start; 
    System.out.println("Getting stack trace took " + duration + " ms"); 

      start = System.currentTimeMillis(); 

    a = 1; 
    for (int i = 0; i < trials; i += 1) { 
     a += 1; 
     (new Throwable()).getStackTrace(); 
    } 

    duration = System.currentTimeMillis() - start; 
    System.out.println("Getting throwable stack trace took " + duration + " ms"); 
+0

de acuerdo con el código fuente, yo diría que currentThread es responsable de la diferencia – njzk2

+2

Este punto de referencia está roto. javac/JVM lo optimizará en la medida en que theat the 1st y 2nd loops se eliminen por completo. Además, millis no se puede usar aquí. – Roman

+3

"Obtener el seguimiento de la pila tomó 29564 ms" es una afirmación absurda; el código de prueba indica que tomó 2,900 ** nano * segundos. Además @Roman tiene toda la razón sobre la optimización de los resultados de alteración. – gerardw

0

Ahora con JDK 9 & 10, puede utilizar StalkWalker, que no es una llamada caro.

private void invoke006() { 
     var stack = StackWalker.getInstance(StackWalker.Option.SHOW_HIDDEN_FRAMES).walk((s) -> s.collect(Collectors.toList())); 
     stack.forEach(stackFrame -> { 
      if (stackFrame.getMethodName().equals("masterInvoker")) { 
       System.err.println("master called !!"); 
       System.err.println(StackWalker.getInstance().walk((s) -> s.collect(Collectors.toList())).get(0).getMethodName() + ", line: " + StackWalker.getInstance().walk((s) -> s.collect(Collectors.toList())).get(0).getLineNumber()); 
      } 
     }); 
    } 
Cuestiones relacionadas