背景

运维人员反馈一个容器化的java程序每跑一段时间就会出现OOM问题,重启后,间隔大概两天后复现。

问题调查

一查日志

由于是容器化部署的程序,登上主机后使用docker logs ContainerId查看输出日志,并没有发现任何异常输出。 使用docker stats查看容器使用的资源情况,分配了2G大小,也没有发现异常。

二缺失的工具

打算进入容器内部一探究竟,先使用docker ps 找到java程序的ContainerId

,再执行docker exec -it ContainerId /bin/bash进入容器。进入后,本想着使用jmap、jstack 等JVM分析命令来诊断,结果发现命令都不存在,显示如下:

bash: jstack: command not found
bash: jmap: command not found
bash: jps: command not found
bash: jstat: command not found

突然意识到,可能打镜像的时候使用的是精简版的JDK,并没有这些jVM分析工具,但是这仍然不能阻止我们分析问题的脚步,此时docker cp命令就派上用场了,它的作用是:在容器和宿主机之间拷贝文件。这里使用的思路是:拷贝一个新的jdk到容器内部,目的是为了执行JVM分析命令,参照用法如下:

Usage:  docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-
docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH [flags]

有了JVM工具,我们就可以开始分析咯。

三查GC情况

通过jstat查看gc情况

 bin/jstat -gcutil 1 1s

看样子没有什么问题,full gc也少。再看一下对象的占用情况,由于是容器内部,进程号为1,执行如下命令:

bin/jmap -histo 1 |more

发现ByteBuffer对象占用最高,这是异常点一。

四查线程快照情况
  • 通过jstack查看线程快照情况。
 bin/jstack -l 1 > thread.txt

下载快照,这里推荐一个在线的线程快照分析网站。

https://gceasy.io

上传后,发现创建的线程近2000个,且大多是TIMED_WAITING状态。感觉逐渐接近真相了。 点击详情发现有大量的kafka-producer-network-thread | producer-X 线程。如果是低版本则是大量的ProducerSendThread线程。(后续验证得知),可以看出这个是kafka生产者创建的线程,如下是生产者发送模型:

根据生产者的发送模型,我们知道,这个sender线程主要做两个事,一是获取kafka集群的Metadata共享给多个生产者,二是把生产者送到本地消息队列中的数据,发送至远端集群。而本地消息队列底层的数据结构就是java NIO的ByteBuffer。

这里发现了异常点二:创建过多kafka生产者。

由于没有业务代码,决定写一个Demo程序来验证这个想法,定时2秒创建一个生产者对象,发送当前时间到kafka中,为了更好的观察,启动时指定jmx端口,使用jconsole来观察线程和内存情况,代码如下:

nohup java -jar -Djava.rmi.server.hostname=ip
-Dcom.sun.management.jmxremote.port=18099
-Dcom.sun.management.jmxremote.rmi.port=18099
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false -jar
com.hyq.kafkaMultipleProducer-1.0.0.jar 2>&1 &

连接jconsole后观察,发现线程数一直增长,使用内存也在逐渐增加,具体情况如下图:

故障原因回顾

分析到这里,基本确定了,应该是业务代码中循环创建Producer对象导致的。

在kafka生产者发送模型中封装了 Java NIO中的 ByteBuffer 用来保存消息数据,ByteBuffer的创建是非常消耗资源的,尽管设计了BufferPool来复用,但也经不住每一条消息就创建一个buffer对象,这也就是为什么jmap显示ByteBuffer占用内存最多的原因。

总结

在日常的故障定位中,多多使用JDK自带的工具,来帮助我们辅助定位问题。一些其他的知识点:

jmap -histo显示的对象含义:

[C 代表  char[]
[S 代表 short[]
[I 代表 int[]
[B 代表 byte[]
[[I 代表 int[][]

如果导出的dump文件过大,可以将MAT上传至服务器,分析完毕后,下载分析报告查看,命令为:

./mat/ParseHeapDump.sh active.dump  org.eclipse.mat.api:suspects
org.eclipse.mat.api:overview org.eclipse.mat.api:top_components

可能尽快触发Full GC的几种方式

1) System.gc();或者Runtime.getRuntime().gc();

2 ) jmap -histo:live或者jmap -dump:live。
这个命令执行,JVM会先触发gc,然后再统计信息。
3) 老生代内存不足的时候

一次容器化springboot程序OOM问题探险的更多相关文章

  1. .NET 7 SDK 开始 支持构建容器化应用程序

    微软于 8 月 25 日在.NET官方博客上,.NET 7 SDK 将包括对创建容器化应用程序的支持,作为构建发布过程的一部分,从而绕过需要.显式 Docker 构建阶段. 这一决定背后的基本认知是简 ...

  2. python + docker, 实现天气数据 从FTP获取以及持久化(五)-- 利用 Docker 容器化 Python 程序

    背景 不知不觉中,我们已经完成了所有的编程工作.接下来,我们需要把 Python 程序 做 容器化 (Docker)部署. 思考 考虑到项目的实际情况,“持久化天气”的功能将会是一个独立的功能模块发布 ...

  3. Rio手把手教学:如何打造容器化应用程序的一站式部署体验

    11月19日,业界应用最为广泛的Kubernetes管理平台创建者Rancher Labs(以下简称Rancher)宣布Rio发布了beta版本,这是基于Kubernetes的应用程序部署引擎.它于今 ...

  4. 利用 ELK 搭建 Docker 容器化应用日志中心

    利用 ELK 搭建 Docker 容器化应用日志中心 概述 应用一旦容器化以后,需要考虑的就是如何采集位于 Docker 容器中的应用程序的打印日志供运维分析.典型的比如SpringBoot应用的日志 ...

  5. 详解利用ELK搭建Docker容器化应用日志中心

    概述 应用一旦容器化以后,需要考虑的就是如何采集位于Docker容器中的应用程序的打印日志供运维分析.典型的比如SpringBoot应用的日志 收集.本文即将阐述如何利用ELK日志中心来收集容器化应用 ...

  6. 2019 DevOps 必备面试题——容器化和虚拟化

    原文地址:https://medium.com/edureka/devops-interview-questions-e91a4e6ecbf3 原文作者:Saurabh Kulshrestha 翻译君 ...

  7. ASP.NET Core使用Docker进行容器化托管和部署

    一.课程介绍 人生苦短,我用.NET Core!今天给大家分享一下Asp.Net Core以Docker进行容器化部署托管,本课程并不是完完全全的零基础Docker入门教学,课程知识点难免有没覆盖全面 ...

  8. 从K8S部署示例进一步理解容器化编排技术的强大

    概念 Kubernetes,也称为K8s,生产级别的容器编排系统,是一个用于自动化部署.扩展和管理容器化应用程序的开源系统.K8s是一个go语言开发,docker也是go语言开发,可见go语言的是未来 ...

  9. springboot项目容器化

    创建一个简单的springboot项目,依赖中加入: 编写一个Restfull接口: 编写启动类: 启动项目,浏览器访问该接口,得到想要的结果.下面,就将这个项目进行Docker容器化(applica ...

随机推荐

  1. python-day16

    一.正则表达式 regular expression -----regex 验证匹配正则表达式使用单个字符串来描述.匹配一系列匹配某个句法规则的字符串.在很多文本编辑器里,正则表达式通常被用来检索.替 ...

  2. 2017版 Jetbrains 系列软件激活方法

    1. 到网站 http://idea.lanyus.com/ 获取注册码. 2.填入下面的license server: http://intellij.mandroid.cn/ http://ide ...

  3. Ms Sql server 主键并发冲突解决办法

    问题原因,同事把表的自增长ID主键,改成是自定义的编号,没有严格的并发测试.在多线程同时插入数据时会引起主键冲突: 看了一些道友的文章,尝试了几个方法.经测试 使用 WITH(updlock)  可以 ...

  4. seq2seq通俗理解----编码器和解码器(TensorFlow实现)

    1. 什么是seq2seq 在⾃然语⾔处理的很多应⽤中,输⼊和输出都可以是不定⻓序列.以机器翻译为例,输⼊可以是⼀段不定⻓的英语⽂本序列,输出可以是⼀段不定⻓的法语⽂本序列,例如: 英语输⼊:&quo ...

  5. Python Web Flask源码解读(一)——启动流程

    关于我 一个有思想的程序猿,终身学习实践者,目前在一个创业团队任team lead,技术栈涉及Android.Python.Java和Go,这个也是我们团队的主要技术栈. Github:https:/ ...

  6. 设计模式(C#)——03建造者模式

    推荐阅读:  我的CSDN  我的博客园  QQ群:704621321       当一个复杂对象由一些子对象构成,并且子对象的变化会导致复杂对象的修改.这时我们需要提供一种"封装机制&qu ...

  7. Unity Editor已停止工作

    在更换系统之后,可能会出现打开刚安装好的Unity,显示Unity Editor已停止工作,这时候我们考虑是系统win7的问题.可以在原系统上升级,也可以重新安装,升级.文中所涉及到的软件,可在右侧加 ...

  8. three.js实现球体地球2018年全球GDP前十国家标记

    概况如下: 1.SphereGeometry实现自转的地球: 2.THREE.Math.degToRad,Math.sin,Math.cos实现地图经纬度与三位坐标x,y,z之间的转换: 3.Imag ...

  9. JavaScript 数据结构与算法之美 - 你可能真的不懂递归

    1. 前言 算法为王. 排序算法博大精深,前辈们用了数年甚至一辈子的心血研究出来的算法,更值得我们学习与推敲. 因为之后要讲有内容和算法,其代码的实现都要用到递归,所以,搞懂递归非常重要. 2. 定义 ...

  10. Shiro的几个关键类

    Shiro在于Spring集成中,需要配置SecurityManager,Realm,ShiroFilterFactoryBean这三个类.在Web环境中SecurityManager一般配置Defa ...