> 文章列表 > 记一次javaMetaspace导致CPU200%的排查

记一次javaMetaspace导致CPU200%的排查

记一次javaMetaspace导致CPU200%的排查

记一次javaMetaspace导致CPU200%的排查

    • 1、场景
    • 2、装arthas
    • 3、分析代码
    • 4、罪魁祸首

1、场景

insertMotionDataByWxCallBack方法并发多(其实也没多少,可能就3个?)就导致CPU200%了,本地没法复现。
看报错是:java.lang.OutOfMemoryError: Metaspace,刚开始的时候眼挫,忽略了后面的Metaspace,只看到了OutOfMemoryError,就各种找代码问题。

2、装arthas

https://arthas.aliyun.com/doc/install-detail.html

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.