目录
疑点restClient在报出异常时没有正确执行关闭导致内存泄漏
分析路线2:这种手动调用是否有必要,不会自动执行资源释放吗?
前言
线上内存报警,重启之后内存占用降低,但是慢慢升高且几乎没有下降迹象,最终流量高峰时触发内存阈值,导致报警,前面通过内存排查工具已经进行过分析,今天从代码的角度的展开排查其中一点,以确定是否导致内存泄漏。
疑点restClient在报出异常时没有正确执行关闭导致内存泄漏
关键部分未改动前代码
try {
.....省略
Response response = restClient.performRequest(HttpGet.METHOD_NAME, endpoint, headerMap);
String result = EntityUtils.toString(response.getEntity());
JSONObject data = JSON.parseObject(result);
restClient.close();
if(null != result && !result.isEmpty()) {
return data.getJSONObject("_source");
}
}catch (Exception e){
log.error("ESClient.get exception:{}", e.getMessage());
}
其中restClient为org.elasticsearch.client.RestClient
线上异常:
初步分析:
如果执行
Response response = restClient.performRequest(HttpGet.METHOD_NAME, endpoint, headerMap);
那么,会导致try中的后面的语句无法执行,也就是说restClient.close();没有得到执行,导致内存泄漏。这个分析看似合理,
于是果断修改代码如下,并重新上线
try {
.....省略
Response response = restClient.performRequest(HttpGet.METHOD_NAME, endpoint, headerMap);
String result = EntityUtils.toString(response.getEntity());
JSONObject data = JSON.parseObject(result);
restClient.close();
if(null != result && !result.isEmpty()) {
return data.getJSONObject("_source");
}
}catch (Exception e){
log.error("ESClient.get exception:{}", e.getMessage());
}finally {
try {
restClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
上线之后,看似起了效果,但是有不那么明显,接下来需要仔细分析源代码了。
基于源代码分析
分析路线1:经过哪些调用,最终关闭了什么?
s1:调用到RestClient#close,内部是CloseableHttpAsyncClient#close
public class RestClient implements Closeable {
private final CloseableHttpAsyncClient client;
...
public void close() throws IOException {
this.client.close();
}
...
}
s2:实际调用到CloseableHttpAsyncClientBase#close,进一步调用到NHttpClientConnectionManager#shutdown
abstract class CloseableHttpAsyncClientBase extends CloseableHttpPipeliningClient {
private final Log log = LogFactory.getLog(this.getClass());
private final NHttpClientConnectionManager connmgr;
private final Thread reactorThread;
private final AtomicReference<CloseableHttpAsyncClientBase.Status> status;
......
public void close() {
if (this.status.compareAndSet(CloseableHttpAsyncClientBase.Status.ACTIVE, CloseableHttpAsyncClientBase.Status.STOPPED) && this.reactorThread != null) {
try {
this.connmgr.shutdown();
} catch (IOException var3) {
this.log.error("I/O error shutting down connection manager", var3);
}
try {
this.reactorThread.join();
} catch (InterruptedException var2) {
Thread.currentThread().interrupt();
}
}
}
public boolean isRunning() {
return this.status.get() == CloseableHttpAsyncClientBase.Status.ACTIVE;
}
static enum Status {
INACTIVE,
ACTIVE,
STOPPED;
private Status() {
}
}
}
此处补充一张CloseableHttpPipeliningClient继承关系图,便于理解其中的实现继承层次
s3: 实际调用到PoolingNHttpClientConnectionManager#shutdown,进一步调用到CPool#shutdown
@Contract(
threading = Threadin