1、场景
insertMotionDataByWxCallBack方法并发多(其实也没多少,可能就3个?)就导致CPU200%了,本地没法复现。
看报错是:java.lang.OutOfMemoryError: Metaspace,刚开始的时候眼挫,忽略了后面的Metaspace,只看到了OutOfMemoryError,就各种找代码问题。
2、装arthas
cd /opt/apps/arthas/
curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar
选择进程
dashboard
# 查看top3的进程号
thread -n 3
- 然后发现,当cpu200的时候,根据挂载不上arthas
- 建议用:https://club.kdcloud.com/article/180683020100514560?productLineId=29
top -b -d2 -n3 -H -p 1 | grep 'top -' -A 17 > slow.log && jstack -l 1 >> slow1.log
- 然后下载下来搜索进程号
- 然后发现没什么用(可能是我不会用?)如果真的要用top+jstack,一定要谨慎谨慎谨慎,我把机器搞崩了,你没听错,是机器,不是java进程,机器直接挂了,ping ip都ping不通,只好断电重启,后面分析系统日志,是内存爆了
3、分析代码
- 没去找Metaspace的问题,而去找代码了,没想到还真找到一个问题,但是cpu200不是这个引起的,这里也记一下
- 分析了一通代码后,发现可能是文件流没关的原因:
- 原始代码如下:
motionApprovalReturnVO.getFileMap()如下:
private Map<String, InputStream> fileMap = new HashMap<>();
List<ContentValue.File> files = content.getValue().getFiles();
if (CollectionUtils.isNotEmpty(files)) {
List<String> fileIds = files.stream().map(ContentValue.File::getFileId).collect(Collectors.toList());
fileIds.forEach(fileId -> {
try {
File download = cpService.getMediaService().download(fileId);
motionApprovalReturnVO.getFileMap().put(download.getName(), new FileInputStream(download));
} catch (Exception e) {
LOGGER.info("getMediaService download:media_id:{} 下载失败", fileId);
}
});
}
- 在后面调用alioss上传也没有close资源
private List<ILabAttachment> pullWxFile2Oss(Map<String, InputStream> fileMap) {
if (MapUtils.isEmpty(fileMap)) {
return new ArrayList<>();
}
Map<String, String> fileUrls = ossService.uploadByStream(fileMap);
return buildILabAttachment(fileUrls);
}
private List<ILabAttachment> buildILabAttachment(Map<String, String> fileUrls) {
if (MapUtils.isEmpty(fileUrls)) {
return new ArrayList<>();
}
List<ILabAttachment> iLabAttachments = new ArrayList<>();
fileUrls.forEach((fileName, url) -> {
ILabAttachment iLabAttachment = new ILabAttachment();
iLabAttachment.setName(fileName);
iLabAttachment.setUrl(url);
iLabAttachments.add(iLabAttachment);
});
return iLabAttachments;
}
- 原本是想着批量上传能快一点,好心办坏事,忽略了close FileInputStream
- 修改一下代码:
List<ContentValue.File> files = content.getValue().getFiles();
if (CollectionUtils.isNotEmpty(files)) {
List<String> fileIds = files.stream().map(ContentValue.File::getFileId).collect(Collectors.toList());
fileIds.forEach(fileId -> {
try {
File download = cpService.getMediaService().download(fileId);
try (FileInputStream inputStream = new FileInputStream(download)) {
// 处理文件流
String fileOssUrl = ossService.uploadByStream(inputStream, download.getName());
motionApprovalReturnVO.getFileUrlMap().put(download.getName(), fileOssUrl);
download.delete();
} catch (IOException e) {
// 异常处理
LOGGER.error("try (FileInputStream inputStream = new FileInputStream(download)) error");
}
} catch (Exception e) {
LOGGER.error("getMediaService download:media_id:{} 下载失败", fileId);
}
});
}
- 用java7中的try-with-resource 语法来自动释放
- 然后发现依然不是这里的问题,如果是文件导致的不应该是java.lang.OutOfMemoryError: Metaspace
4、罪魁祸首
- 后来本地 用VisualVM监控了下程序,发现Metaspace有70M了,而服务器上jvm启动参数只有64M!!!!!!
- 原来的
-XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=128m
修改后
-XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m
- 真实奇耻大辱,下次眼睛还是认真一点看吧
end.