Java性能优化战术-日志
日志框架被设计为记录程序日志消息,并对应用自身和服务器负载影响极小,但使用不谨慎,仍然会对应用和服务器造成影响. 高并发系统需要谨慎使用日志功能,本节介绍使用日志框架的一些常见误区。
下列代码来自两个日志的Facade框架SLF4J和Common Log,前者有占位符,后者有字符串拼接,你觉得哪个好,为什么Spring源码中使用了后者?
log.info(" order id {},user id {}",order.getId(),order.getUserId());
//Spring使用
log.info(" order id "+order.getId()+",user id "+order.getUserId());Spring使用后者的主要原因是性能问题,前者的占位符使得程序更容易读懂,但占位符解析需要耗费时间。
再看看SLF4J关于info的接口和定义,你觉得前两个方法的定义是多余的吗,只要Object... var2即可?
void info(String var1, Object var2);
void info(String var1, Object var2, Object var3);
void info(String var1, Object... var2);答案:对于 Object... var2,var2是一个一维数组。在调用此方法前,字节码自动构造数组作为入参,SLF4J从性能优化角度,分别定义了当参数有且只有一个和参数只有两个的API方法,避免创建数组。
//你认为运行的java
log.info("....",para0,para1,pars2);
//实际运行的java
Object[] array= new Object[3];
array[0]=para0,array[1]=para1,array[2]=para2;
log.info("",array)在实际项目中,对象转化为String耗时会导致日志调用方法的耗时,比如如下输出就非常耗时:
log.debug("hell0 "+JSON.toJSONString(myHugeObject));
log.debug("hell0 "+ myHugeObject.toString()); //toString也可能带来潜在问题因为在调用log.debug方法前,需要把myHugeObject对象转化为JSON字符串作为Debug方法参数,这是一个非常耗时的操作,可能远远超过了调用log方法本身。可以通过判断日志级别来避免这种情况:
if(log.debugEnable()){
log.debug("hell0 "+JSON.toJSONString(myHugeObject));
}或者仅输出需要打印的信息:
log.debug("hell0 "+myHugeObject.getId()+":"+myHugeObject.getName());其他可能由日志框架或者使用不当引起的问题如下
- 日志框架配置输出日志有到Console。即System.out。 因为Console是一个较慢的IO设备(TODO),所以可能引起性能慢或者调用阻塞
- 日志框架输出时候,包含调用行和所在方法,日志框架必须通过抛出RuntimeException(参考5.12.2 谨慎使用Exception),导致CPU耗费。 日志框架也会解析异常栈包含的类和方法信息,也会导致性能下降
- 日志框架通常使用队列缓冲日志信息,大并发量情况下,队列满的处理策略如果是抛弃,则不会引起调用阻塞,如果是强制入队,或者使用调用者线程处理日志,则可能导致阻塞。但是抛弃低级别的日志,会丢失观察数据。
- 大量输出日志,会对磁盘I/O产生影响,特别是日志配置了压缩功能,日志在压缩的时刻,会耗费CPU资源。笔者遇到的一个系统就曾经每隔一段时间,请求响应时间变长,观察发现CPU使用率非常高,然而请求量在正常范围内。后来才发现每当日志量达到一定阈值时,就会压缩日志。去掉日志压缩功能后,系统就恢复正常
- 日志包含了输出异常栈,或者一些较大对象,也可能对磁盘IO有影响
- 为了避免大量日志出现,甚至需要对打印日志限流,比如调用下游服务,当出现异常的时候,通常会打印详细的错误信息
try{
call();
}catch(Exception ex){
log.error("xxx",ex);
}考虑到如果下游长时间出问题,有会产生大量日志,因此需要对此限流
try{
call();
}catch(Exception ex){
if(rateLimter.tryAcquire()){
log.error("xxx",ex);
}
}由于日志是用来设计记录高并发访问量的一个系统,因此要求日志框架使用者需要关注日志框架的最佳使用实践和版本升级说明,否则,在系统大并发下,不恰当的日志配置,会导致日志框架可能成为系统性能瓶颈
