使用SWIG对接Java与C++时容器环境下stringmatrix内存泄漏问题
问题复现前提
依赖版本:javac 1.8.0.291、gcc 7.4、swig 3.0.8
现象:非容器环境下嵌套vector(stringmatrix)的内存分配、释放流程正常,容器环境下native内存持续增长直至进程崩溃,单独使用一维vector(stringvector)无该问题。
根因说明
- SWIG 3.0.x版本的
std_vector.i对嵌套容器的Java封装存在所有权语义缺陷:当内层stringvector被存入外层stringmatrix后,C层外层vector已经持有内层vector的所有权,Java侧通过s.get(i)获取的是指向C层内层vector的新代理对象,主动调用该代理对象的delete()方法会直接释放C++层外层vector持有的内存,后续外层vector执行delete()时会再次遍历释放内层元素,触发双重释放。 - 容器环境下glibc默认的malloc配置不会主动将释放的内存归还操作系统:默认MALLOC_ARENA_MAX为CPU核心数*8,容器分配的CPU核心数较多时会产生大量内存 arena,已释放的内存会存在于各arena的自由链表中无法被跨arena复用,从操作系统视角看内存持续上涨。非容器环境下glibc的内存trim阈值更低,不会触发该表象。
修复方案
- 无需调整现有SWIG接口文件的vector定义,仅需明确内存所有权逻辑即可,原接口定义无需修改:
%include "std_string.i" %include "std_vector.i" %{ #include <vector> %} namespace std { %template(stringvector) std::vector<std::string>; %template(stringmatrix) std::vector< std::vector<std::string> >; }
- 简化Java侧内存释放逻辑,删除内层元素释放操作,避免双重释放:
public static void ReleaseStringMatrix ( stringmatrix s ) { s.delete(); }
- 容器启动Java进程前配置glibc malloc参数,强制内存及时归还操作系统:
export MALLOC_ARENA_MAX=2 export MALLOC_TRIM_THRESHOLD_=131072
- 修正Java侧
BuildMatrix方法的笔误:将s_data.set ( s, s1 );改为s_data.set(i, s1);,避免未定义行为。 - 可选升级SWIG到4.0及以上版本,该版本修复了嵌套std::vector的Java封装内存管理已知缺陷。
内容的提问来源于stack exchange,提问作者FatherJack
相关产品推荐
相关产品推荐

