记一次javaMetaspace导致CPU200%的排查

Administrator
发布于 2023-04-23 / 264 阅读 / 1 评论 / 0 点赞

记一次javaMetaspace导致CPU200%的排查

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
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.


评论