性能调优Python集成Java的服务OOM问题分析经历
Posted sysu_lluozh
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了性能调优Python集成Java的服务OOM问题分析经历相关的知识,希望对你有一定的参考价值。
一、问题背景
在一个遍历服务中,同时运行5个设备执行遍历任务时,快速出现如下错误
容器ExitCode :137, 原因:OOMKilled
该服务是通过python程序唤起执行Java任务
二、资源占用
既然是OOM,得确认资源占用情况
- 服务的资源占用
发现系统中总内存使用较低,且python和Java进程的内存使用不高,那为什么会出现OOM的情况呢?
注:其实这里查看的是整个服务器的内存占用,该服务器内容总共25G,所以内存占用不高
- 查看容器的内存配置
容器的最大内存500M
但是从这些信息中并未得到较高的内存占用
三、python内存分析
使用memory_profiler分析python的内存使用
from memory_profiler import profile
@profile
def compatibility_task(task_id):
# do compatibility job
python task.py
获取具体的执行信息
内存占用200M以内,应该问题不大(这里其实有个大坑,导致后面折腾一通)
四、Java内存分析
既然不是python的性能问题,那么接下来分析一下java的内存
使用命令获取hprof文件
jmap -dump:format=b,file=/app/temp/test11.hprof pid
很开心的使用MAT工具分析dump文件
发现内存占用正常没有问题
配置OOM时自动触发headdump
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/app/temp/heapdump.hprof
在OOMKilled时并未触发生成dump文件,那是否是因为并不是Java程序的OOM呢?
五、进程内存使用分析
好吧。还是需要详细分析究竟是哪个进程的内存占用的问题
同时执行5个设备遍历,在运行时获取各个进程的内存使用
ps aux | awk 'print $6/1024 " MB\\t\\t" $11' | sort -n
得到信息
0 MB COMMAND
0.00390625 MB /pause
0.667969 MB sleep
0.972656 MB awk
2.29688 MB sort
3.34766 MB /bin/bash
3.41406 MB /bin/bash
3.66016 MB ps
4.05469 MB top
9.15625 MB java
53.5156 MB java
487.68 MB python3.6
竟然是python的程序内存撑满导致容器直接OOM
六、python源码分析
回到python内存分析这一步,为什么在内存分析中并未找到问题呢?
问题在于获取内存数据是在本地调试执行,且执行的是1个设备执行遍历任务
还是不能偷懒,先撸一遍遍历时python的执行逻辑吧,简化的相关逻辑如下:
- 从redis拉取任务
- 监测需要执行的任务数
- 最高拉起20个线程同时执行
比如任务列表中需要执行的任务数10,那么接下来10个线程每个线程都处理的任务如下:
- 解析任务信息
- 执行各自下载apk操作
- 初始化环境操作
- 下发安装操作
- cmd执行遍历服务jar包
- 处理并获取报告
- 环境清理处理
从python内存分析中也可以看到
Line # Mem usage Increment Occurences Line Contents
============================================================
137 58.6406 MiB 58.6406 MiB 1 @profile(precision=4, stream=open('memory_profiler_traverse_test.log', 'w+'))
......
184 59.1328 MiB 0.0000 MiB 1 if not app_config_info:
185 Logger.logger.info('不存在该应用配置,已跳过,请在配置页面添加该应用配置')
186 return
192
193 193.1602 MiB 134.0273 MiB 1 install_result, install_time = self.app_install_exec(package_name, apk_url)
194 193.1602 MiB 0.0000 MiB 1 print("install_time: ", install_time)
195 # 测试应用启动时间
196 193.1602 MiB 0.0000 MiB 1 start_result, app_start_time = self.get_app_start_time(package_name, app_config_info.get("main_activity"))
197 193.1602 MiB 0.0000 MiB 1 self.yaml_file_name = compatibility_yaml_name.format(self.device_id + "_" + str(app_config_info.get("id")))
198 193.1602 MiB 0.0000 MiB 1 Logger.logger.info("yaml file: ".format(self.yaml_file_name))
199 193.1641 MiB 0.0039 MiB 1 app_performance = AppPerformance(self.subtask_id, self.device_id, package_name)
......
在安装操作self.app_install_exec(package_name, apk_url)
时内存飙升,那么着重看一下安装的代码逻辑:
def app_install(self, download_url):
get_response = requests.get(download_url, timeout=60)
if get_response:
app_content = get_response.content
res = requests.post(
"http://:/mobile//installApp".format(self.agent_ip, self.agent_port, self.device_id),
files=
"app": app_content
)
OMG!安装的过程是直接requests.get将数据包下载下来,然后再将包内容content直接下发给手机设备mobile执行安装操作
这种处理方式太诡异了,有几个问题:
- 多线程执行时包其实基本是同样的,但是都执行了同样的下载操作,为什么不做合并处理?
- 下载时直接将apk包load在内存对象中然后再转发给mobile,如果包较大且多个同时下载时内存是否可以支持?
- 有链接地址,为什么不传链接地址让mobile直接下载安装呢?
七、问题修复
其实现在问题已经很明显了,后面验证了5台、10台、20台设备时再次验证了从源码中的分析的原因
问题修复方式只要将apk包的链接地址传给mobile,mobile自行下载然后执行安装逻辑即可
以上是关于性能调优Python集成Java的服务OOM问题分析经历的主要内容,如果未能解决你的问题,请参考以下文章
2022-02-26-Spark-46(性能调优SparkUI)