记一次Java内存泄漏最终导致内存溢出的事故分析
admin
2024-01-28 11:20:53
0

参考

  • Java的内存回收机制详解
  • java中内存泄露8种情况的总结
  • Java 内存泄漏原因、解决办法及泄漏排查
  • Java 内存泄漏排查,新技能Get
  • java内存调试工具(5)
  • jvisualvm.exe
  • java内存泄露排查总结
  • VIRT持续增长,记一次内存泄漏定位
  • 如何查看JAVA某个进程下的线程
  • 给线程池里面线程添加名称的4种方式
  • Java进程内存无法释放

Java内存监控工具

jps

//获取 java进程的 pid
jps -l

jstat

虚拟机统计信息监视工具

//每2秒 获取一次 gc 信息
jstat -gcutil pid 2000

jmap

//查看存活对象情况 导出日志信息
//查看内存信息 实例个数 以及 占用内存大小
jmap -histo:live pid > ./livelog.txt
//查看年轻代, 老年代 内存配置 和 使用情况
//查看内存堆栈信息
//8.0以前
jmap -heap pid > ./log.txt//8.0以后
jhsdb jmap --heap --pid pid > ./heaplog.txt

jstack

//打印堆栈信息
jstack -l pid > ./stacklog.txt

事故处理经过

最近用户量激增 后台说我这个推送服务 占用CPU和内存激增

top 指令查看 显示我的java进程 占用200%的CPU和30G的虚拟内存

虽然我是Android开发 但是同时负责推送服务器的维护,仅仅是维护 平时就加些功能 新接入加厂商推送之类 并没有悉知的读过代码

这次异常 不得不让我从头过一遍源码

解决CPU占用异常

重头过一遍源码后 每次下发厂商推送消息都会从数据库查一下该用户绑定的厂商推送信息

如果每次推送都查询数据库 肯定是不行的 于是加了个缓存

用 ConcurrentHashMap 做了个缓存队列 将用户id和厂商推送信息绑定

当要下发推送时先找缓存队列里有没有 有就直接从缓存里拿 没有再查 查到之后加到缓存队列里

按照这个思路修改后 重新部署到服务器

CPU降下来了 从 200% 降到了 30%-40%左右

这个肯定还有待优化的地方 但是目前问题已经解决

解决虚拟内存占用过高

接下来就是内存占用过高的

经过实时监控发现 刚启动程序虚拟内存只占用了 900M

紧接着已肉眼可见的速度增长 当涨到40G也就是分配的磁盘空间时 就产生OOM

为什么会 内存溢出 因为系统已经没有内存分配给这个程序

也就是说 程序里什么线程或者对象没有释放掉 导致一直占用内存 也就是内存泄漏 最终导致内存溢出

想到这里 我先用

jps -l

查看程序的进程的 pid

然后 用

jmap -histo:live pid > ./livelog.txt

打印了 当前进程下 内存的实际占用情况

发现并没有什么异常 整个进程数据占用的内存只有400M左右

接下来 使用

jstack -l pid > ./stacklog.txt

打印堆栈信息

不看不知道 一看吓一跳

好家伙 3000个苹果推送的线程 锁死 和 4000个google推送的线程 锁死

并且还在不断上涨 也就是说 每发一条google推送 或 苹果推送 就会生成一个线程来执行这个任务

经过进一步观察 苹果的还好 APNS的库 用的okhttp3 我将 OkHttpClient 单独提出来 所有请求公用一个 OkHttpClient 避免的不停的创建

也就是说 之前每发一条苹果推送 就会创建一个 OkHttpClient 而默认超时是60秒 如果网络波动或者发送异常 就会锁死60秒 等 OkHttpClient 超时后 才会释放掉这个 线程 而我们每秒有几百条请求 所以导致不断积累

不过这些比起来google推送的几万条就是小巫见大巫了

苹果推送的解决了 接下来是 google推送的

和苹果推送同样的策略 但是我发现google推送是已经分装好的

FirebaseMessaging messaging = FirebaseMessaging.getInstance(app);
messaging.send(msgObj);

使用 FirebaseMessaging 直接 send 要下发的消息即可 所以无从下手

再梳理一番发现 由于我们国内的阿里云无法访问国际互联网 导致

google推送鉴权失败 也就是说 每下发一条 google推送 如果没有鉴权 会先去鉴权 由于无法访问google导致鉴权耗时60秒 在这期间 线程会锁死 这还没玩 当google的库发现鉴权超时 会直接报错 但是不知道为什么 FirebaseMessaging无法释放 导致 线程一直锁死

也就是说 google的推送会一直占用 并不会像苹果的推送一样 超时会释放

怎么解决呢 我发现google的库版本太低 是不是库本身的bug 我决定将库提升到最新版

升级了 google 的库 从6.0.0 升级到 目前9.1.1的版本

com.google.firebasefirebase-admin9.1.1

重新测试 发现果然是库的问题

最新版的库 也就是60秒超时后 可以正常释放 也就取消了线程的占用

但是还是会锁死60秒 这个不能忍

索性 国内禁用掉google推送 反正也收不到 开启没啥P用啊

直接在推送服务器启动前 先判断 能否访问 google 的 80端口

如果能访问 说明在国外 正常初始化 google推送的sdk

如果不能访问 说明在国内 则禁用google推送

public static boolean isOnline() {Socket server;try {server = new Socket();InetSocketAddress address = new InetSocketAddress("www.google.com", 80);server.connect(address, 3000);System.out.println("google 可用!");return true;} catch (UnknownHostException e) {System.out.println("google 解析异常!");e.printStackTrace();} catch (IOException e) {System.out.println("google 连接超时!");e.printStackTrace();}return false;
}

重新打包 上线测试

这波真万事大吉 虚拟内存从20G降到9000M

经过一天的测试 发现没有虚拟内存一直保持在正常范围内 没有暴涨 CPU也正常 打印堆栈信息 也没发现线程锁死

至此 该事故已完美解决

最后

当然 该系统还有待优化的地方 但这都是后话了。

对于一个做Android的 初入后端 这次事故排查让我学到了很多

这次从 线程池 内存泄漏 内存溢出 高并发 等 收获颇丰

我这篇文章 仅仅是展示解决思路 公司代码也不能贴出 所以多多包涵

大家如果遇到类似的问题 可以先看 我上面贴出的参考资料

这些资料帮了我很多(虽然在大量的复制粘贴文章里感觉就像是 屎里淘金)

可以先看看这些参考文章 再看看我的解题思路 应该有所帮助!

报错处理

当你使用 jmap -heap pid 出现如下报错

参考-Java内存相关的常用命令

Type "GenericGrowableArray", referenced in VMStructs::localHotSpotVMStructs in the remote VM, was not present in th
e remote VMStructs::localHotSpotVMTypes table (should have been caught in the debug build of that VM). Can not continue.

这是由于你的默认JAVA_HOME和程序使用Java的版本不一致导致的

可以使用绝对路径 来访问

C:\Users\itloser\.jdks\openjdk-17.0.1\bin\jmap -heap 6672

接下来你可能会遇到

-heap option used
Cannot connect to core dump or remote debug server. Use jhsdb jmap instead

这表示你使用的Java版本是8.0以后的需要使用

参考-查看jvm用到的一些命令

C:\Users\itloser\.jdks\openjdk-17.0.1\bin\jhsdb jmap --heap --pid 6672

相关内容

热门资讯

明代的科举制度是怎样的? 明代的科举制度是怎样的?明代,生员参加每三年一次在省会举行的乡试,考中的称举人;举人参加每三年一次(...
婚后恋爱的言情小说 婚后恋爱的言情小说不懂说将来,艾米写得很好看。
要如何查有没有人偷我家的电? 要如何查有没有人偷我家的电?把自己家里的所有电器全部关闭,看看是否自己的电表是否还在运行,如果你的电...
用微笑掩饰,悲伤会没有吗? 用微笑掩饰,悲伤会没有吗?不会变,但是至少不会带给别人,你控制住了他的蔓延,当你知道自己没有让自己的...
后羿和杨戬哪个厉害,嫦娥更喜欢... 后羿和杨戬哪个厉害,嫦娥更喜欢谁,那些骗分的别回答你们说的都是废话,我不喜欢听废话嫦娥到底喜欢谁是她...
香辣土豆片的美味 在忙碌的生活中,我们总是渴望找到那些简单又美味的家常菜,既能满足味蕾,又能节省时间。今天,我要和大家...
原汁原味安徽菜!香菇鸡翅根,鲜... 安徽风味的香菇鸡翅根是一道家常又下饭的菜,鸡肉鲜嫩,香菇吸饱了汤汁,香味浓郁,吃起来特别入味。这道菜...
原创 晚... 一、夜晚饮食的特殊性 夜晚对于人体来说是一个特殊的时段,身体在经过一天的活动后,逐渐进入休息和修复的...
原创 老... 在乡村的怀抱中,美食不仅是味蕾的盛宴,更是心灵的慰藉。今天,我有幸为我的丈夫准备了一顿丰盛的晚餐,共...
8道早餐美食,让家人拥有好脾胃... 8道早餐美食,让家人拥有好脾胃口~ 一日之计在于晨,而早餐则是开启活力满满的一天的关键 “钥匙”;一...
原创 咖... 说到日本美食,很多人想到的是寿司、拉面、寿喜锅等食物。 但是如果你在日式料理店里,也能找到法式、意...
叶无心端木孤辰是哪部小说里的 叶无心端木孤辰是哪部小说里的《 穿越,第九个王妃 》,雪色水晶写的。
杨宗保死而复生是穆桂英挂帅第几... 杨宗保死而复生是穆桂英挂帅第几集38集死而复生
蒙太奇是什么意思?有哪些种类? 蒙太奇是什么意思?有哪些种类?RT蒙太奇(法语:Montage)是音译的外来语,原为建筑学术语,意为...
4月23日,是世界读书日,班里... 4月23日,是世界读书日,班里准备在 这天下午3点,在教室开展关于读书的主题班会。作为活动组织者,你...
中医里面,把药研细末炼蜜为丸,... 中医里面,把药研细末炼蜜为丸,是怎么炼蜜为丸了?。。。。先把药材磨成细粉,再把蜂蜜用小火煮,也就是炼...
新还珠格格晴儿的配音为什么和赵... 新还珠格格晴儿的配音为什么和赵丽颖本音那么像?今晚无聊打开看了看 怎么好像是她自己的声音 然后紫薇好...