内存溢出的日常

事情是这样的,正开开心心的写着代码
突然线上报警了,内存告急,可用内存不足1G

告警

???
第一反应是也没干啥啊,咋就报警了,然后运维同学抛出了一张图

内存可用率

线上应用的可用内存,就像中学边上那条长长的下坡路,急转直下…
不出意外是内存泄露了,80%还是我写的代码

头皮发麻

抓紧联系运维同学,dump了两份间隔半小时的内存快照
然后赶紧重启了服务器,先让它好起来

接着开始用MAT分析快照,定位了是一些流没有被关闭,导致内存一直缓慢增长
好了,到了这里才是今天的正文
下面是错误写法,DefaultHttpClient也已经被标记成了 @Deprecated ,而且client和response也没有close

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static String sendGet(String url) {
String res = null;
try {
DefaultHttpClient client = new DefaultHttpClient();
HttpGet request = new HttpGet(url);
HttpResponse response = client.execute(request);
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
res = EntityUtils.toString(response.getEntity());
}
}catch (IOException e) {
logger.error("get请求提交失败:{}", url, e);
}
return res;
}

然后改成了这个样子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static String sendGet(String url) {
String res = null;
try (CloseableHttpClient client = HttpClients.createDefault()) {
HttpGet request = new HttpGet(url);
try (CloseableHttpResponse response = client.execute(request)) {
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
res = EntityUtils.toString(response.getEntity());
}
}
} catch (IOException e) {
logger.error("get请求提交失败:{}", url, e);
}
return res;
}

问题到了这里,本来就完事了,突然想看看这个EntityUtils是干啥的?

EntityUtils

一共这些方法,toString()已经知道了,那这个consume()是干啥的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* Ensures that the entity content is fully consumed and the content stream, if exists,
* is closed.
*
* @param entity the entity to consume.
* @throws IOException if an error occurs reading the input stream
*
* @since 4.1
*/
public static void consume(final HttpEntity entity) throws IOException {
if (entity == null) {
return;
}
if (entity.isStreaming()) {
final InputStream instream = entity.getContent();
if (instream != null) {
instream.close();
}
}
}

哦,等等,这是个关流的方法啊,我之前可是都没调用过啊,这不又漏了!!!

赶紧又去仔细看了看toString()

toString

原来这里已经关闭过了,那没事了