Apache Gluten内存泄漏排查:使用Valgrind定位原生代码问题的方法

📅 2026/7/27 17:47:28 👁️ 阅读次数 📝 编程学习
Apache Gluten内存泄漏排查:使用Valgrind定位原生代码问题的方法

Apache Gluten内存泄漏排查:使用Valgrind定位原生代码问题的方法

【免费下载链接】glutenGluten is a middle layer responsible for offloading JVM-based SQL engines' execution to native engines.项目地址: https://gitcode.com/GitHub_Trending/glu/gluten

Apache Gluten作为将JVM-based SQL引擎执行卸载到原生引擎的中间层,其内存管理对系统稳定性至关重要。原生代码内存泄漏可能导致Spark Executor因内存超限被杀死,本文将介绍如何使用Valgrind工具快速定位Gluten项目中的C++内存泄漏问题。

内存泄漏的常见表现与危害

在Gluten中,内存泄漏通常表现为:

  • Spark Executor进程异常退出并提示内存超限
  • 长时间运行任务后OffHeap内存持续增长
  • 间歇性OOM错误且难以复现

特别是当原生内存不受Gluten管理时,传统Java内存分析工具无法捕获泄漏点,此时需要Valgrind等原生代码调试工具介入。

Valgrind工具准备与环境配置

安装Valgrind

在Ubuntu/Debian系统中通过apt直接安装:

apt install valgrind

对于CentOS/RHEL系统:

yum install valgrind

编译调试版本的Gluten

为获得准确的内存泄漏追踪信息,需要编译包含调试符号的Gluten原生代码:

# 编译Velox后端并启用测试和调试模式 gluten_home/dev/builddeps-veloxbe.sh --build_tests=ON --build_benchmarks=ON --build_type=Debug

使用Valgrind检测内存泄漏的步骤

编写针对性的单元测试

根据docs/developers/NewToGluten.md建议,创建GoogleTest测试用例来隔离可能存在泄漏的代码路径。例如创建exec_backend_test测试可执行文件,专注测试 shuffle 或计算逻辑。

执行Valgrind内存检查

使用Valgrind的memcheck工具运行测试:

valgrind --leak-check=yes ./exec_backend_test

关键参数说明:

  • --leak-check=yes:启用内存泄漏检测
  • --show-reachable=yes:显示可达内存块(潜在泄漏)
  • --track-origins=yes:追踪未初始化值的来源

分析Valgrind输出结果

Valgrind会生成详细的内存泄漏报告,包括:

  • 泄漏内存总量和块数
  • 每个泄漏点的调用栈
  • 内存分配位置和大小

典型的泄漏报告类似:

==12345== LEAK SUMMARY: ==12345== definitely lost: 1,024 bytes in 4 blocks ==12345== indirectly lost: 4,096 bytes in 16 blocks ==12345== possibly lost: 0 bytes in 0 blocks ==12345== still reachable: 8,192 bytes in 32 blocks ==12345== suppressed: 0 bytes in 0 blocks

结合内存分析工具定位问题

使用gperftools生成内存使用热力图

Gluten项目推荐使用gperftools辅助内存分析,通过以下命令生成内存使用GIF图:

pprof --show_bytes --gif --lib_prefix=/path/to/gluten_lib_prefix /usr/bin/java /path/to/gluten_heap_perf_XXX > result.gif

图1:使用gperftools生成的内存分配热力图,可直观展示内存使用热点

分析内存分配文本报告

通过文本模式查看详细内存分配统计:

pprof --text --lib_prefix=/path/to/gluten_lib_prefix /usr/bin/java /path/to/gluten_heap_perf_XXX

图2:内存分配统计报告,显示各函数内存分配占比

常见内存泄漏场景与解决方案

场景1:Arrow内存分配器泄漏

当看到类似以下日志时:

WARN ArrowBufferAllocators$ArrowBufferAllocatorManager: Detected leaked Arrow allocator [Default], size: 191...

可启用Arrow内存调试:

-Darrow.memory.debug.allocator=true

场景2:C++对象未正确释放

通过Valgrind定位到具体泄漏函数后,检查:

  • 是否所有new分配的对象都有对应的delete
  • 智能指针使用是否正确(如std::unique_ptr/std::shared_ptr
  • 资源获取即初始化(RAII)模式是否被遵守

场景3:JNI层内存管理不当

检查JNI接口实现:

  • jobject引用是否正确释放
  • 原生内存是否通过NewGlobalRef/DeleteGlobalRef管理
  • 避免在JNI回调中分配长期内存

验证修复效果

修复后应:

  1. 重新编译调试版本
  2. 再次运行Valgrind检查确认泄漏已解决
  3. 执行端到端测试确保功能正常
  4. 使用jemalloc生成内存快照进行对比:
jeprof --text --lib_prefix=/path/to/gluten_lib_prefix /usr/bin/java /path/to/gluten_heap_perf_XXX

总结

使用Valgrind配合gperftools和jemalloc,能有效定位Gluten项目中的原生代码内存泄漏问题。关键步骤包括:

  • 编译调试版本代码
  • 编写针对性测试用例
  • 分析Valgrind泄漏报告
  • 结合内存分析工具可视化问题
  • 验证修复效果

通过这套方法论,可以显著提升Gluten的内存管理质量,避免因内存泄漏导致的生产环境故障。完整的内存调试文档可参考docs/developers/ProfileMemoryOfGlutenWithVelox.md。

【免费下载链接】glutenGluten is a middle layer responsible for offloading JVM-based SQL engines' execution to native engines.项目地址: https://gitcode.com/GitHub_Trending/glu/gluten

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考