本周技术问题总结--2017.09.01

目录

 

一、idea打包关闭test

二、log4jslf4j包冲突

三、user.name配置与系统环境变量重名失效

四、jvm内存报警

 

日常工作中经常会遇到一些问题,会花大量时间去解决,但时间一长又会遗忘,以后不定期收集每周遇到问题,进行整理收集。以下是本周遇到的几个问题:

 

一、idea打包关闭test

本周项目处于调试阶段,在调试过程中经常出现数据库表里的测试数据被删、或被修改的情况。严重影响测试进度,刚开始是怀疑有同事人为的对数据进行修改,几经排查发现是由于在使用idea编译打包过程中,会自动执行代码工程中单元测试方法,单元测试方法中有对数据库表记录的增删改查操作。部分精心准备的测试数据,在每次执行maven package的时候被篡改。解决办法:在打包时关闭单元测试,关闭方法点开ideamaven面板,点击一个闪电标记即可,如下图:



 

 

 

二、log4jslf4j包冲突

 

冲突包为log4j-over-slf4j.jarslf4j-log4j12.jar,现象为tomcat启动失败,错误日志内容为:

java.lang.StackOverflowError

Caused by: java.lang.IllegalStateException: Detected both log4j-over-slf4j.jar AND slf4j-log4j12.jar on the class path

 

从日常上看是log4j-over-slf4j.jarslf4j-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.jarlog4j调回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配置与系统环境变量重名失效

 

好心网友sxp2558SkySchedulehttp://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 GCParallel收集器没有找到类似的参数。同事最终把参数改为:

-XX:+UseConcMarkSweepGC -XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection -XX:CMSInitiatingOccupancyFraction=65

具体含义为:采用cms垃圾收集器;开启碎片整理;5full GC后进行一次碎片整理;年老代内存超过65%,自动进行full GC

 

由于cms垃圾收集器都cpu要求很敏感,修改jvm参数后,再进一步观察下 根据情况决定是否切回Parallel垃圾收集器。

猜你喜欢

转载自moon-walker.iteye.com/blog/2391903