最近碰到的三个Case

上周接连在处理三个Case,处理的有点焦头烂额,比较郁闷,先给大家分享下,如果有碰过类似case或有解决方案的同学,欢迎回复下。

Case I
第一个Case是一个跑在Tomcat的应用,现象就是应用没响应,负载也很低,jstack出来的信息显示tomcat的http-apr-*-exec的线程都处于同样的状态:
java.lang.Thread.State: RUNNABLE
org.apache.tomcat.jni.Socket.recvbb
对Tomcat APR不怎么熟,于是纯粹是借助google来排查这个问题,翻了好几页,发现多数都是碰到这个现象,但没有解决的描述,只好去翻代码了,从相应的代码来看,觉得这个地方不太可能一直被卡住才对,并且这里还是有超时的,dump内存分析了下当时的超时参数,发现也是有的,陷入更加不知道解决的境地了。

还好第二天有另外一个在排查这个问题的同事做了一个尝试,把apr模式换成了bio模式(熟悉tomcat的人会知道tomcat connector主要有bio/nio/apr三种模式,但貌似没找到很详细的介绍tomcat实现这几种模式的文章,如果有人知道,欢迎回复下),然后可以看到这些线程都卡在了
java.net.SocketInputStream.socketRead0,对bio熟多了,所以可以知道是因为建了较多的连接,都卡在了读事件上了,那其实解决方法就可以是把线程数开大点。

为什么应用会突然出现连接增多的现象,是因为前面增加了一堆的机器,但我挺难理解的是apr模式为什么会出现线程数由于连接数增加就耗满的现象,理论上如果是nio类型的模式的话,增加的一点连接数其实是不会有什么太大的压力的(因为通常是用很少的线程数来处于连接和读写事件的),看起来我觉得tomcat在这块的实现比netty貌似差了不少,不过对这块不算非常熟悉,还不能完全判断。

ps: 另外一个同时之前还碰到过前面在等tomcat返回,而tomcat又在等前面发数据的死锁bug,但具体信息已经不太记得了。

Case II
一个依赖JMagick的应用,在新的一个环境部署后出现Could not initialize class magick.ImageInfo的现象,但没有其他更多的信息,排查的时候主要是检查JMagick对应的jar、native的相关的包是否存在,折腾了N次,而且也确认jar、native以及依赖的native包都存在,但还是没解决…

后来这个问题突然就好了,而且更奇怪的是,lsof去查的时候发现native包还是没装载,但其他的机器是装载了的,神奇…

这个Case如果你有什么排查的建议的话,欢迎回复…

Case III
一个应用出现OOM,dump下来的内存分析后显示有个巨大的char[]数组,而这个char[]里面的信息是非常长的堆栈信息等,而且看起来还没什么关联,非常奇怪,触发这个char[]的异常的地方看起来比较容易理解,就是有代码在线程里执行this.wait的时候竟然没调用synchronized(this),这个错误有点低级了,所以问题算是能解决。

但我一直没想明白的就是这个巨大的char[]是从哪生成出来的,还好的是AliJDK里默认就打开了当生成巨大的array的时候打印相应的堆栈(非Ali JDK没这功能),但诡异的是从堆栈来看,这个信息抛出的地方非常随机,貌似唯一的共同点就是在打错误日志,这个现在还没太多的头绪,后面如果有进展再写篇东西吧。

=============================
欢迎关注微信公众号:hellojavacases

关于此微信号:
分享Java问题排查的Case、Java业界的动态和新技术、Java的一些小知识点Test,以及和大家一起讨论一些Java问题或场景,这里只有Java细节的分享,没有大道理、大架构和大框架。

公众号上发布的消息都存放在http://hellojava.info上。

双11前来三个case的分享

最近堪称问题爆发的高发期,不过有不少是重复的cases,挑三个分享下。

Case I
有个应用cpu sy消耗很高,现象是线程数太多,但现场已经没有了,在出现问题的时候有做过线程的dump和内存dump,从线程dump看到的就是大量的线程池的线程在等待,但最郁闷的又是没有名字的线程池,于是无法从这个堆栈看出来到底是什么地方造成的,对这种问题,最好的办法是有现场,然后直接btrace跟踪下是哪个地方在不断的创建线程。

既然没有现场,就尝试拿着内存dump分析下,看了那些创建的N多的线程,从中发现了一个点,就是线程的线程组名,从这个线程组名上对应到了代码的名字,根据这个知道了是哪个地方造成的问题。

这个后来查到的原因是一个类里每次会new ThreadPoolExecutor,原因是开发有些误解,以为这个类只会new一次,这种理解上的问题很容易导致一些超出预期的运行状况,不过对于线程池,我始终认为,既然是用线程池,那还是static的比较合理。

Case II
一个跑在tomcat里的应用启动后报错,报错的信息当时没保留,大概类似如下:
This is very likely to create a memory leak.
看的完全无法理解,觉得非常诡异…

后来翻查了下tomcat自己的日志(我们的系统默认是分开了tomcat的日志以及应用的日志),发现其实是应用启动的时候失败了,相应的解决后重启一切正常。

从这点来说,tomcat的报错还是忒诡异了点,值得改进。

Case III
又碰到一个netty误用的case,还是每次创建netty客户端的连接时,NioClientSocketChannelFactory都每次new,这就导致了Netty每次都会创建Boss和Worker线程,所以好的做法都是static创建一次。

不过这个地方犯错已经不是见过一次两次了,因此我觉得Netty的API还是有必要考虑一下这点的设计。

从这些cases能看到,代码上,尤其是API的使用,要慎之又慎,否则一些小错误在大并发量的情况下会暴露的非常明显。

=============================
欢迎关注微信公众号:hellojavacases

关于此微信号:
分享Java问题排查的Case、Java业界的动态和新技术、Java的一些小知识点Test,以及和大家一起讨论一些Java问题或场景,这里只有Java细节的分享,没有大道理、大架构和大框架。

公众号上发布的消息都存放在http://hellojava.info上。