对大量日志产生导致Java后端某些类加载错误问题记录
报错问题:
java.lang.IllegalArgumentException: Name for argument type [java.lang.String] not available, and parameter name information not found in class file either.
org.springframework.util.Assert.notNull(Assert.java:112)
原因分析:
因后端日志级别开得很低,debugger级别,导致日志大量进行读写操作,占用大量内存,内存不足,导致某些类没法加载,经过代码分析和查阅相关资料,归类为一下几种:
- 有些日志打印使用的试logj4形式,导致大量字符串不断创建,而且写入日志又频繁,GC又来不及回收,导致堆内存溢出,类加载不到
- e.printStackTrace() 将异常打印到控制台时,会将产生错误堆栈信息存入字符串常量池中,如果在常量池空间较小且异常较多时,常量池空间可能会被异常信息占满,这样其他需要使用或者正在使用此空间的线程就会产生阻塞现象,甚至最终抛出 OOM,导致整个应用挂掉。(经排查程序中确在日志打印的时候直接调用了printStackTrace)
- 由于log4j版本问题导致StringBuilder调用一直在产生字符串对象,没有及时清除导致 具体文章分析
log4j和Slf4j的区别
log4j:Apache的一个开源项目,可以灵活地记录日志信息,我们可以通过Log4j的配置文件灵活配置日志的记录格式、记录级别、输出格式,而不需要修改已有的日志记录代码。
slf4j:slf4j不是具体的日志解决方案,它只服务于各种各样的日志系统。是一个日志门面,允许最终用户在部署其应用时使用其所希望的日志系统。
可以将log4j看成是一个完整的日志库,而slf4j是一个日志库的规范接口
日志级别
日志级别从低到高:TRANCE、DEBUG、INFO、WARNING、ERROR、CRITICAL
说明:
-
TRANCE:最详细的信息,一般这些信息只记录到日志文件中。
-
DEBUG:详细的信息,通常只出现在诊断问题上
-
INFO:确认一切按预期运行
-
WARNING:一个迹象 表明,一些意想不到的事情发生了,或表明一些问题在不久的将来(例如。磁盘空间低”)。这个软件还能按预期工作。
-
ERROR:更严重的问题,软件没能执行一些功能
-
CRITICAL:一个严重的错误,这表明程序本身可能无法继续运
那么使用slf4j有什么好处呢?
- 让日志和项目之间解耦
想象一下这种场景,目前我们的项目已经使用了log4j作为日志库,有一天我们引入了一个技术大牛编写的组件,但是这个组件使用的是logback来进行日志输出,那么问题就来了,我们就不得不需要添加两个实现同样功能的jar包并且维护两套日子配置。
而slf4j 是一个适配器,我们通过调用slf4j的日志方法统一打印我们的日志,而可以忽略其他日志的具体方法,这样,当我们的系统换了一个日志源后,不需要更改代码
-
节省内存
log4j这些传统的日志系统里面并没有占位符的概念,当我们需要打印信息的时候,我们就不得不创建无用String对象来进行输出信息的拼接。比如日志级别为info,而程序中使用如下打印日志,则不会创建字符串,即:log4j打印日志级别如果低于系统设置的级别的话,则不会产生字符串创建带来的开销
logger.debug("错误信息为:{}",errormsg);
总结:
1. 在使用日志记录问题时应该注意使用方式,以及使用级别,日志不是越多越好,打出关键错误点就行
2. 在生产中日志级别应该尽量调高点,以免频繁产生大量的日志导致内存问题,或者将JVM内存参数调高,这样某些类日志级别可以调低,方便排查
3. 项目初期最好使用slf4j来做日志记录,方便对日志进行统一维护,避免引入多余的日志类