事情是这样的,正开开心心的写着代码,突然线上报警了,内存告急,可用内存不足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是干啥的?
一共这些方法,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()
原来这里已经关闭过了,那没事了。