当前位置: 代码网 > it编程>数据库>Mysql > HDFS文件浏览器功能OOM排查

HDFS文件浏览器功能OOM排查

2024年07月31日 Mysql 我要评论
涉及HDFS文件浏览器的某个功能运行一段时间后会出现OOM的情况,根据dump文件排查问题,最终在代码中定位,由于调用函数不当导致,通过新增一个map用于缓存去重的缓存方式解决。

现象描述

涉及hdfs文件浏览器的某个功能运行一段时间后会出现oom的情况
错误日志如下:

service.log.2023-02-01-0.log:java.lang.outofmemoryerror: java heap space

排查过程

需要查看dump文件排查一下造成oom的原因
查看jvm参数如下:

java -duser.timezone=asia/shanghai 
# -xms:初始堆大小 512m
-xms512m 
# -xmx:最大堆大小 1024m
-xmx1024m 
# -xx:metaspacesize 元空间最小尺寸128m,初次分配,表示metaspace首次使用不够而触发fgc的阈值,只对触发起作用
-xx:metaspacesize=128m 
# -xx:maxmetaspacesize 元空间最大尺寸512m
-xx:maxmetaspacesize=512m 
# -xx:+heapdumponoutofmemoryerror 表示当jvm发生oom时,自动生成dump文件
-xx:+heapdumponoutofmemoryerror 
# -xx:+printgcdatestamps 输出gc的时间戳(以jvm启动到当期的总时长的时间戳形式)
-xx:+printgcdatestamps 
# -xx:+printgcdetails 输出详细gc日志
-xx:+printgcdetails 
# 年轻代(包括eden和两个survivor区)与年老代的比值(除去持久代,设置为4,则年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5
-xx:newratio=1 
# 设置年轻代中eden区与survivor区的大小比值。设置为4,则两个survivor区与一个eden区的比值为2:4,一个survivor区占整个年轻代的1/6
-xx:survivorratio=30 
# 使用线性垃圾回收器
-xx:+useparallelgc
# 使用 parallel scavenge收集器的老年代版本,使用多线程和标记-整理算法。 
-xx:+useparalleloldgc 

可以发现并没有指定dump文件存储路径,默认存储在启动该jar的用户根目录下,当前是root用户,从/root路径下将dump文件(java_pid25034.hprof)下载到本地进行分析

  • 安装memoryanalyzer对dump文件进行分析
    在这里插入图片描述
    发现所有实例对象中,concurrenthashmap占比达到47%,实例数10100193,确定它应该是问题所在。软件自动定位到其中占比最多的一个实例是来源于distributedfilesystem的值,以此为关键字在代码中进行排查。

原因溯源

定位到代码中发现是之前为解决“两个hdfs集群namespace同名时会造成两个集群读取到同一套文件系统”的问题,对命名空间库中已经存在的命名空间,由调用filesystem.get(hadoopconfig)方法换为调用filesystem.newinstance方法新建实例。这样确实能解决这个问题,但是为什么会造成oom呢?

查看hdfs源码中的filesystem.newinstance源码:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
我们发现在底层方法中会有一行向静态变量cache下的map中put值的代码,该类使用公共类加载器,而此类加载器加载过的所有 放在堆中的class实例都被回收完了, 此类加载器才会被回收,静态变量只有在类加载器被回收时才会被回收,也就意味着该cache以及其下的map一直不会被回收。

因此我们可以看出,解决问题的人希望通过newinstance函数每次都产生新实例的方式解决两个集群namespace同名时混淆的问题,修改后每次调用该函数都会调用newinstance函数,由于对所用到的hdfs源码底层逻辑掌握不够清晰,引入了新的问题,最终导致在需要定时多次调用该服务的在业务场景下oom情况的发生。

解决方案

使用一个hashmap将每个hdfs对应的filesystem都缓存起来,在创建之前判断缓存map中是否存在,如果存在则从缓存中取出该实例,不存在则新建并放入map。使用hashmap的computeifabsent实现。


private final map<string, filesystem> filesystemmap = new concurrenthashmap<>();
//逻辑改为以下,每个集群的hadoopconfig都是不同的可用来标识该集群
return filesystemmap.computeifabsent(ns, value -> newfilesystem(value, hadoopconfig));
(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com