本周技术问题总结--2017.09.01
目录
一、idea打包关闭test
二、log4j与slf4j包冲突
三、user.name配置与系统环境变量重名失效
四、jvm内存报警
日常工作中经常会遇到一些问题,会花大量时间去解决,但时间一长又会遗忘,以后不定期收集每周遇到问题,进行整理收集。以下是本周遇到的几个问题:
一、idea打包关闭test
本周项目处于调试阶段,在调试过程中经常出现数据库表里的测试数据被删、或被修改的情况。严重影响测试进度,刚开始是怀疑有同事人为的对数据进行修改,几经排查发现是由于在使用idea编译打包过程中,会自动执行代码工程中单元测试方法,单元测试方法中有对数据库表记录的增删改查操作。部分精心准备的测试数据,在每次执行maven package的时候被篡改。解决办法:在打包时关闭单元测试,关闭方法点开idea的maven面板,点击一个闪电标记即可,如下图:
二、log4j与slf4j包冲突
冲突包为log4j-over-slf4j.jar和slf4j-log4j12.jar,现象为tomcat启动失败,错误日志内容为:
java.lang.*Error
Caused by: java.lang.IllegalStateException: Detected both log4j-over-slf4j.jar AND slf4j-log4j12.jar on the class path
从日常上看是log4j-over-slf4j.jar和slf4j-log4j12.jar冲突了,冲突原因分析:
首先看下slf4j-log4j12.jar(转接到log4j日志框架,正向过程,官网:官网https://www.slf4j.org/manual.html#swapping)
slf4j不是日志框架,而是日志打印规范,log4j是日志框架负责具体的日志打印(类似流行的还有logback,据说比log4j的性能更好)。他们的关于有点类似于jvm规范和Hotspot的关系,一个是规范,一个是具体的实现。
这里slf4j是规范,封装了各种日志打印规范,比如可以使用占位符打印日志: logger.error("{}登陆成功,登陆时间{}",userid,“2017-09-01”)。最终具体的日志打印操作交给log4j处理。
这时就需要引入slf4j-log4j12.jar,依赖关系为:slf4j-api.jaràslf4j-log4j12.jaràlog4j.jar。
如果有一天你想切换到性能更好的logback日志框架,只需把slf4j-log4j12.jar包替换为logback即可。
再来看下log4j-over-slf4j.jar(log4j调回slf4j,逆向过程,官网:https://www.slf4j.org/legacy.html)
使用log4j-over-slf4j取代log4j,这样log4j接口输出的日志就会通过log4j-over-slf4j路由到SLF4J上,这样即使系统(包含使用的第三方jar库,比如dubbo)都可以将日志最终路由到SLF4J上,进而集中输出。这个依赖关系为:log4j-over-slf4j.jar à slf4j-api.jar,可以看到这个依赖关系刚好与上述相反,导致出现无限相互递归依赖,最终导致溢出。
参考:
http://blog.csdn.net/kxcfzyk/article/details/38613861?utm_source=tuicool
http://blog.csdn.net/john1337/article/details/76152906
三、user.name配置与系统环境变量重名失效
好心网友sxp2558在SkySchedule(http://moon-walker.iteye.com/blog/2386504)过程中发现一个bug:笔者在配置netty连接使用的用户名时,在properties文件中配置的key为” user.name”,代码中配置的值为“moon” 但实际使用的时候通过spring上下文Environment获取到的值为当前服务器的“主机名”,导致SkySchedule的客户端无法与服务端连接成功。
根本原因为:” user.name”为默认系统常量,配置在properties中的值会被覆盖,导致读取到的是“主机名”,而不是properties文件中配置的用户名。
目前已经改为“sky.user.name”,代码已提交到github。再次感谢sxp2558。
四、jvm内存报警
本周项目所属系统出现几次jvm内存报警,内存使用率超过80%。
首先分析系统采用垃圾回收算法:年轻代采用的ParNew收集器,年老代采用的Parallel Old收集器,都是采用的并行收集器,jdk版本是1.8。
再看下GC情况,没有触发full gc,只有少量young GC,系统访问量也不高。初步分析是每次年轻代GC存活下来的对象较多,堆积到年老代的对象日益增加,同时有没有触发full GC导致。
解决办法:
1、可以写个定时任务,每晚凌晨2点,业务闲时调用system.gc() 触发full gc。
2、当内存使用到达一定的百分比时,自动触发full GC。
第一种办法,需要重新上线,暂时没有采纳。
第二种办法,可以把年老代改用CMS收集器,CMS收集器有个CMSInitiatingOccupancyFraction参数,可以控制当年老代内存使用到达一定的比值,自动触发full GC。Parallel收集器没有找到类似的参数。同事最终把参数改为:
-XX:+UseConcMarkSweepGC -XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection -XX:CMSInitiatingOccupancyFraction=65
具体含义为:采用cms垃圾收集器;开启碎片整理;5次full GC后进行一次碎片整理;年老代内存超过65%,自动进行full GC
由于cms垃圾收集器都cpu要求很敏感,修改jvm参数后,再进一步观察下 根据情况决定是否切回Parallel垃圾收集器。
上一篇: AI零基础五步临摹简单可爱MBE风格的火焰渐变图标教程
下一篇: PHP基础4--函数-数组