ideadump分析⼯具_实战:OOM后我如何分析解决的
点击上⽅“匠⼼零度”,选择“设为星标”
做积极的⼈,⽽不是积极废⼈
作者:jasonGeng88
现在很多⾯试官都会关⼼你是否有过解决内存泄漏的问题,是否有过JVM的调优经验。你如果没有经历过,该如何回答呢?希望下⽂对你有所帮助。
背景
前不久,上线了⼀个新项⽬,这个项⽬是⼀个压测系统,可以简单的看做通过回放词表(http请求数据),不断地向服务发送请求,以达到压测服务的⽬的。在测试过程中,⼀切还算顺利,修复了⼏个⼩bug后,就上线了。在上线后给到第⼀个业务⽅使⽤时,就发现来⼀个严重的问题,应⽤⼤概跑了10多分钟,就收到了⼤量的 Full GC 的告警。
针对这⼀问题,我们⾸先和业务⽅确认了压测的场景内容,回放的词表数量⼤概是10万条,回放的速率单机在 100qps 左右,按照我们之前的预估,这远远低于单机能承受的极限。按道理是不会产⽣内存问题的。
线上排查
⾸先,我们需要在服务器上进⾏排查。通过 JDK ⾃带的 jmap ⼯具,查看⼀下 JAVA 应⽤中具体存在了哪些对象,以及其实例数和所占⼤⼩。具体命令如下:
jmap -histo:live `pid of java`
# 为了便于观察,还是将输出写⼊⽂件
jmap -histo:live `pid of java` > /tmp/jmap00 经过观察,确实发现有对象被实例化了20多万,根据业务逻辑,实例化最多的也就是词表,那也就10多万,怎么会有20多万呢,我们在代码中也没有到对此有显⽰声明实例化的地⽅。⾄此,我们需要对 dump 内存,在离线进⾏进⼀步分析,dump 命令如下: jmap -dump:format=b,file=heap.dump `pid of java
离线分析
从服务器上下载了 dump 的 heap.dump 后,我们需要通过⼯具进⾏深⼊的分析。这⾥推荐的⼯具有 mat、visualVM。 我个⼈⽐较喜欢使⽤ visualVM 进⾏分析,它除了可以分析离线的 dump ⽂件,还可以与 IDEA 进⾏集成,通过 IDEA 启动应⽤,进⾏实时的分析应⽤的
CPU、内存以及GC情况(GC情况,需要在visualVM中安装visual GC 插件)。⼯具具体展⽰如下(这⾥仅仅为了展⽰效果,数据不是真的):
当然,mat 也是⾮常好⽤的⼯具,它能帮我们快速的定位到内存泄露的地⽅,便于我们排查。展⽰如下:
场景再现 经过分析,最后我们定位到是使⽤ httpasyncclient 产⽣的内存泄露问题。 httpasyncclient 是 Apache 提供的⼀个 HTTP 的⼯具包,主要提供了 reactor 的 io ⾮阻塞模型,实现了异步发送 http 请求的功能。 下⾯通过⼀个 Demo,来简单讲下具体内存泄露的原因。 httpasyncclient 使⽤介绍:
1.maven 依赖
org.apache.httpcomponents
httpasyncclient4.1.3
2.HttpAsyncClient 客户端
public class HttpAsyncClient {
private CloseableHttpAsyncClient httpclient;
public HttpAsyncClient() {
httpclient = ateDefault();
httpclient.start();
}
public void execute(HttpUriRequest request, FutureCallbackcallback){
}
public void close() throws IOException {
httpclient.close();
}
}
主要逻辑:
Demo 的主要逻辑是这样的,⾸先创建⼀个缓存列表,⽤来保存需要发送的请求数据。 然后,通过循环的⽅式从缓存列表中取出需要发送的请求,将其交由 httpasyncclient 客户端进⾏发送。 具体代码如下:    public class ReplayApplication {
public static void main(String[] args) throws InterruptedException {
//创建有内存泄露的回放客户端
ReplayWithProblem replay1 = new ReplayWithProblem();
//加载⼀万条请求数据放⼊缓存
Listcache1 = replay1.loadMockRequest(10000);//开始循环回放
replay1.start(cache1);
}
}
回放客户端实现(内存泄露):
这⾥以回放百度为例,创建10000条mock数据放⼊缓存列表。回放时,以 while 循环每100ms 发送⼀个请求出去。具体代码如下:    " + StatusLine());
}
public void failed(final Exception ex) {
System.out.RequestLine() + "->" + ex);
}
public void cancelled() {
System.out.RequestLine() + " cancelled");
}
});
i++;
Thread.sleep(100);
}
}
}" >public class ReplayWithProblem {
public ListloadMockRequest(int n){
Listcache = new ArrayList(n);for (int i = 0; i < n; i++) {
HttpGet request = new HttpGet("www.baidu?a="+i);jdk怎么使用
cache.add(request);
}return cache;
}public void start(Listcache) throws InterruptedException {
HttpAsyncClient httpClient = new HttpAsyncClient();int i = 0;while (true){final HttpUriRequest request = (i%cache.size()); ute(request, new FutureCallback() {public void completed(final HttpResponse response) {
System.out.RequestLine() + "->" + StatusLine());
}public void failed(final Exception ex) {
System.out.RequestLine() + "->" + ex);
}public void cancelled() {
System.out.RequestLine() + " cancelled");
}
});
i++;
Thread.sleep(100);
}

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。