2

我在我的应用程序中使用 log4j,只是想知道我是否将一个对象传递到记录器中,如 (1) 中那样,这会比我在 (2) 中Exception传递时消耗更多的资源吗?e.fillInStackTrace()换句话说,(2)会比(1)更便宜吗?下面是我在 log4j 上的示例代码。

private static Logger logger = Logger.getLogger(...);

try {
   ...
}
catch( Exception e ) {
   logger.error("blah blah blah", e);  // (1)

   logger.error("blah blah blah", e.fillInStackTrace());  // (2)
}

我在这里有点困惑,请帮助澄清?

4

2 回答 2

4

好吧,如果您查看fillInStackTrace,您会注意到该方法是synchronized。同步在 Java(或任何语言)中被认为是一项相当昂贵的操作,因为它需要获取和释放监视器。

另外,您应该注意,Throwable返回的 fromfillInStackTrace仅记录有关调用堆栈在调用时的堆栈帧的信息fillInStackTrace。此信息直接从 JVM 收集,然后转换为再次消耗资源的对象表示。(最终,fillInStackTrace委托给一个native扫描调用线程的当前调用堆栈的方法。)

但是,您应该主要考虑要在日志中显示的内容并根据该标准来决定。当您显式调用fillInStackTrace时,当前调用堆栈将出现在您的日志中。这意味着您将不再看到到达您的块的更深try的调用堆栈(考虑您的示例)。

毕竟,除非您明确需要通过(2)获得的信息,否则您应该始终使用变体(1 ) 。我无法自发地想到解决方案(2)有意义的标准日志记录范围内的示例。但是,当您编写某种框架并且想要清除一些与异常无关的顶部堆栈条目时,您可以使用(2)来消除堆栈跟踪中的噪音:例如,您可能正在使用某种ExceptionFactory为您创建抛出的异常。然后,您不希望堆栈跟踪显示这些工厂方法的堆栈帧,因为它们与错误无关,并且会使尝试调试的用户感到困惑。fillInStackTrace因此,您将在收到工厂的异常后通过调用手动填充堆栈跟踪。

看这个例子,直观地了解这两个调用的区别:

class Example {
  public static void main(String[] args) {
    try {
       outerFunction();
    }
    catch (Throwable e) {
       System.err.println("Outside:");
       e.printStackTrace();
    }
  }
  static void outerFunction() throws Throwable {
    try {
       innerFunction();
    } 
    catch(Exception e) {
       System.err.println("Inside:");
       e.printStackTrace();
       throw e.fillInStackTrace();
    }
  }
  static void innerFunction() {
     throw new RuntimeException("A custom exception");
  }
} 

将调用这两个不同的堆栈跟踪:

Inside:
java.lang.RuntimeException: A custom exception
at Example.innerFunction(Example.java:21)
at Example.outerFunction(Example.java:13)
at Example.main(Example.java:19)
Outside:
java.lang.RuntimeException: A custom exception
at Example.outerFunction(Example.java:13)
at Example.main(Example.java:19)
于 2013-08-20T07:54:21.767 回答
2

两个版本之间的区别在于堆栈跟踪包含的内容。

  • 在第一个版本中,它将是原始堆栈跟踪;即用于最初实例化异常的方法调用。

  • 在第二个版本中,它将是当前帧的堆栈跟踪;即对于这个方法调用。


换句话说,(2)会比(1)更便宜吗?

这很难说。一方面,调用fillInStackTrace()是重做之前在Throwable构造函数调用时完成的工作fillInStackTrace()。另一方面,堆栈帧的数量将会减少(因为您将弹出一些堆栈帧)。

然而,这是没有意义的。方法(2)的动机不是(或至少不应该是)关于性能。它是关于您希望在日志文件的堆栈跟踪中出现多少上下文。

于 2013-08-20T08:03:17.723 回答