如何在使用Java登录时避免创建未使用的String实例

时间:2014-08-14 08:33:32

标签: java performance logging log4j

我使用log4j定义一个Util类来记录,方法如下:

public static void info(String msg) {
    if (logger.isInfoEnabled())
        logger.info(msg);
}

但是如果日志级别设置为"错误" (因此logger.isInfoEnabled()false),我使用以下内容进行记录,是否会创建两个String个实例?

LogUtil.info("String instance" + stringParam);

即使这两个String实例可能被gc收集,构造一个实例仍然是成本。

我想如果在调用logger.info(msg)之前检查日志级别可能有帮助,但这可能代码不干净,这就是我定义Util类的原因。

如何编写干净的代码,如果我没有将级别设置为info,它既不会记录任何内容也不会创建String实例?

4 个答案:

答案 0 :(得分:4)

不要定义Util类,你可以避免一些字符串创建开销。

除了记录器本身将进行检查之外,您进行isInfoEnabled检查完全没有意义。

使用:

logger.log(Level.INFO, "Message {0} with {1}", new Object[] { param1, param2 });

您将避免大部分字符串创建开销,因为它只会toString() param1param2并构建最终消息,如果INFO级别已打开。

分配新的Object[]数组需要花费很少的费用

答案 1 :(得分:3)

您还可以从Slf4J记录器(业界新的标准)获益,它具有更友好的语法:

logger.info("Configuration directory: {}, {}", param1, param2);

也可以使用不同的后端配置Slf4J,包括Log4J。希望这有帮助

答案 2 :(得分:3)

我担心你的util类完全没用,因为记录器已在内部进行日志级别检查。在外部手动执行这些检查的想法是减少由准备日志消息的不必要代码(主要是String级联)引起的开销,即使由于当前日志级别没有记录它也是如此:

假设您要记录用户输入 - 这是一个Date对象。在这里,即使日志级别低于 info ,也会完成日期到字符串的转换和字符串连接:

logger.info("user input: " + dateFormatter.format(userInput));

所以解决方法是首先检查日志级别:

if (logger.isInfoEnabled()) {
    logger.info("user input: " + dateFormatter.format(userInput));
}

但是如果您现在将支票移动到一个将完整消息作为参数的utililty方法,那么您最终会遇到与上述相同的问题!


其他人已在答案中解释了如何使用扩展日志方法让记录器在内部进行格式化:

logger.log(Level.INFO, "Current user input: {0}", userInput);

但是在这里,如果参数必须首先进行特殊格式化,它不能解决开销问题。同样,您必须先进行额外的日志级别检查。


Java 8 开始,您可以编写一个实用程序方法,通过使用lambda表达式可以有效且巧妙地使用它:

public static void info(Supplier<String> messageSupplier) {
    if (logger.isInfoEnabled()) logger.info(messageSupplier.get());
}

用法:

LoggerUtil.info(() -> "user input: " + dateFormatter.format(userInput));

这里,只有在启用了信息记录的情况下才会执行lambda表达式!

答案 3 :(得分:1)

即使此字符串实例将由gc收集, -

logger.info("String instance");中,"String instance" String Literal ,将在字符串池中创建。不幸的是,只要程序运行(即直到JVM关闭),它才会持续存在。使用StringBuilderString(char[])来阻止将字符串添加到字符串池。