说来也巧,这几天正在看一本书,叫做《Java系统性能优化实战》,李家智著的,在代码审查这章中提到了一个关于日志输出问题,先看一下我们原本是怎么输出信息的,如下。
private static Logger logger = LoggerFactory.getLogger(FileManageApplication.class);
public static void main(String[] args) {
logger.info("日志输出{}",1);
}
第一个参数是格式化模板,“{}”是占位符,第二个参数是类型可以是Object... arguments
的,会将第一个参数中的"{}"依次替换为这个数组中的数据,不出意外的话,会输出如下信息。
2021年01月07日 19:52:09:INFO main (FileManageApplication.java:35) - 日志输出1
但是他却不建议使用这种方式,理由是日志框架内部会有一个把占位符"{}"替换成目标变量的耗时过程,考虑到info方法可能被频繁调用,对性能有影响,所以建议直接拼接,代码如下:
public static void main(String[] args) {
User user =getUser();
logger.info("用户名:"+user.getUserName());
}
这听起来似乎也有几分道理,但是在今天早上刷到了一篇公众号文章,里面也恰好提到了这个问题(是个某大V),里面这样说:“不要进行字符串拼接,那样会产生很多String对象,占用空间,影响性能”。
what???
并且他给出了一个反例,如下:
logger.debug("Processing trade with id: " + id + " symbol: " + symbol);
并且还有个正例,如下:
logger.debug("Processing trade with id:[{}] and symbol : [{}] ", id, symbol)
另外这样的格式,可读性更好,对于排查问题更有帮助。
所以,最后我们用什么方法?
下面我们还是自己看测试一下把。
时间比较使用拼接方式public class Main {
private static Logger logger = LoggerFactory.getLogger(Main.class);
public static void main(String[] args) {
long startTimer = System.currentTimeMillis();
for (int i = 0; i < 200000; i++) {
logger.info("i="+i);
}
System.out.println(System.currentTimeMillis()-startTimer);
}
}
运行10次,结果是如下,平均是512.2。
534、510、486、518、495、505、526、507、511、530
使用模板方式import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Main {
private static Logger logger = LoggerFactory.getLogger(Main.class);
public static void main(String[] args) {
long startTimer = System.currentTimeMillis();
for (int i = 0; i < 200000; i++) {
logger.info("i={}",i);
}
System.out.println(System.currentTimeMillis()-startTimer);
}
}
运行10次,结果是如下,平均是642.9。
654、648、599、624、678、660、643、656、583、684
确实如书中所说,替换"{}"的过程比较耗时。
这里没使用System.out.println()
做比较,因为他实在太慢了,因为内部会涉及到加锁。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Main extends JFrame {
private static Logger logger = LoggerFactory.getLogger(Main.class);
public Main() {
this.setSize(300, 300);
this.setVisible(true);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
JButton jButton = new JButton("开始");
jButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
for (int i = 0; i < 1000000; i++) {
logger.info("i={}",i);
// logger.info("i="+ i);
}
}
});
this.add(jButton);
}
public static void main(String[] args) {
new Main();
}
}
模板方式点击按钮等待输出完成后,内存停留在112,764KB。
执行GC后降到13,484KB。
拼接方式点击按钮等待输出完成后,内存停留在128,014KB,相差15,250KB。
执行GC后降到17,777KB。
在这次比较中,拼接方式占用的内存较高,也符合公众号中所说。
结论显然都各胜一筹,所以只能看看自己需求了,另外在如Tomcat源码中,使用的也是拼接方式....
还有在阿里巴巴Java开发手册中这样说....
- END -