摘要:首先先解读下这个报警内容,原因活跃线程数过多,是监听的端口号用来获取虚拟机各项信息,代表着此时的线程数,是设置的报警阈值。
前言
前天,一位21世纪的好好青年正在工位上默念社会主义大法好的时候,钉钉上又报警了(公司项目接入了open-faclon监控,指标不正常会报警给钉钉的机器人),无奈默默流泪挥手告别社会主义大法开始定位线上问题。
报警内容首先我们先来看下报警信息,为防止泄露公司信息,对项目名称和监听的端口号进行了抹除:
这个问题曾多次出现,一直没有抓到导致这个问题的鬼。首先先解读下这个报警内容,原因:活跃线程数过多,jmxport是监听的jmx端口号用来获取虚拟机各项信息,5118>=1500:5118代表着此时的线程数,1500是设置的报警阈值。
为什么要将阈值设置为1500呢(如果你对这个阈值的设置并不感兴趣,可直接跳过阅读)?
为了搞清楚这个阈值的建立规则,我可是下了大功夫的....操作系统可以简单的分为32位和64位,每一个32位进程都独享4G的虚拟地址空间,其中低2G是给用户的,高2G是给系统预留的。也就是每一个32位进程中,实际能够使用的内存不到2G。64位下由于寻址操作变为2的64次方基本可以看做没有限制。介绍完这个,就来介绍下博主监控程序的运行环境,4核8GB内存64为linux系统java8,程序的jvm参数为-server -Xms4g -Xmx4g -Xmn2g。-Xss 为jvm启动的每个线程分配的内存大小,如果未设置,则默认是1m(java8)。系统总内存为8G,堆内存设置为了4G,可用给分配线程的内存不会超过4G,理论上可以达到最大的线程数应该是4*1024个(实际会更少,系统文件、元空间等都会占用大量内存)。搜索了大量相关资料,只是简单的计算出了一个理想情况下线程数的最大值,对于阈值设置并没有找到一个很权威的说法。所以博主就大胆猜测,这个阈值应该是运维根据自己的经验设置的,之所以设置这个阈值是为了更好的得知服务器的运行状况,当超过这个数了,我们应当检测其原因,如果是程序代码造成的问题就需要对症下药,如果是环境问题或者流量确实达到这个阈值,我们就需要去考虑做集群做高可用。
如何定位线程过多的原因
jdk给了我们很多好用的工具,如果对jvm一点都没有了解过得同学建议大家读一下我之前的写的jvm相关的学习笔记。首先定位java项目进程pid,然后打印堆栈文件并分析堆栈文件。其过程如下图所示:
接下来我们着重来分析erp.txt这个文件,针对于线程过多的排查主要是查询程序内线程池是否使用得当,我们可以根据是否存在大量相似的线程名来判断。经过分析发现文件中大量存在OkHttp ConnectionPool,如下图所示:
经过匹配,OkHttp Connection达到4000多个,然后再次定位,发现系统中有调用php的代码使用OKhttp,代码如下所示:
public final class HttpUtil { public static String doPost(String url, String content) throws IOException { OkHttpClient client = new OkHttpClient(); RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), content); Request request = new Request.Builder().url(url).post(body).build(); Response response = client.newCall(request).execute(); return response.body().string(); } public static String doGet(String url) throws IOException { OkHttpClient httpClient = new OkHttpClient(); Request request = new Request.Builder().url(url).build(); Response response = httpClient.newCall(request).execute(); return response.body().string(); } }
简单了解下OkHttpClient
线程过多是OkHttpClient导致的,我们就需要对症下药,简单了解下其用法。翻看其源码可以发现如下图注释:
简单翻译下:对于所有的http请求,建议你创造一个OkHttpClient实例并重复使用这是因为每个实例都有它自己的连接池和线程池,重用连接池和线程池可以减少延迟节省内存,下面的是OKHttpClinet的一些常见用法。至此我们已经完全清楚此次问题的来龙去脉,只需要重用OkHttpClient就好....
总结解决这个问题非常简单,只要将OKhttpClient设置为单例,按照推荐用法去创建即可解决,不过这篇博客的价值并不在于此,而是和大家探讨一些解决问题的思路,如果你有更好的办法,欢迎留言。问题解决了,我继续默念社会主义大法好了^.^
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/69161.html
摘要:直接显示了一个疑似内存泄漏的问题。然后分析文件给出的信息,发现一个叫的类。文件里面说的内存泄漏的大概的意思就是说,这个类里面的存放的东西太多了,爆掉了。修改了代码将调用的地方改成了单例。修改完线上跑了一段日子,后来也没有出现过这样的问题。 问题描述: 早上去公司上班,突然就邮件一直报警,接口报异常,然后去查服务器的运行情况,发现java的cpu爆了.接着就开始排查问题 问题解决...
摘要:此时我想到了福尔摩斯说过的一句话当你排除掉各种不可能出现的情况之后,剩下的情况无论多么难以置信,都是真相。福尔摩斯冷静下来想一想,这个线程,有可能静悄悄地退出了吗,没留下半点异常日志从理论上来说,不可能。配置建议最后,附上一份配置建议。 1、事发 我们有个视频处理程序,基于 SpringBoot,会启动几个线程来跑。要退出程序时,会发送一个信号给程序,每个线程收到信号后会平滑退出,等全...
摘要:当然,如果你的核心数够多,到个线程的并行度不满足的话,也可以自定义一个线程池来执行,不过这样的话,要注意自己维护这个线程池的初始化,释放等等操作了。 事情起源于一个bug排查,一个AsyncTask的子类,执行的时候发现onPreExecute方法执行了,doInBackground却迟迟没有被调用。懂AsyncTask一些表面原理的都知道,onPreExecute方法是在主线程执行,...
阅读 1183·2021-11-17 09:33
阅读 3578·2021-09-28 09:42
阅读 3238·2021-09-13 10:35
阅读 2390·2021-09-06 15:00
阅读 2395·2021-08-27 13:12
阅读 3590·2021-07-26 23:38
阅读 1758·2019-08-30 15:55
阅读 522·2019-08-30 15:53