为何引用静态inline map会触发访问违规错误?
你遇到的访问违规问题,核心原因是类级静态成员的初始化顺序问题,属于C++中典型的「静态初始化顺序混乱」变种。
问题根源
你的m_classes是JavaClass的inline static成员,虽然C++17标准规定inline static成员会在第一次使用时懒初始化,但当你在getInstance中调用std::make_shared<JavaClass>(className)时,会触发JavaClass的构造函数。如果此时m_classes的初始化尚未完成(比如构造函数内部间接调用了getInstance,或者编译器重排了静态成员的初始化顺序),就会导致访问未完全初始化的内存,出现地址0x68的解引用错误——这个地址通常是对象内部偏移或未初始化指针的垃圾值。
类级静态成员的初始化时机由编译器决定,若在JavaClass的构造过程中提前访问m_classes,必然会触发未定义行为。
修复方案
方案1:改用局部静态变量缓存(推荐)
将缓存m_classes从类的静态成员改为getInstance函数内的局部静态变量,利用C++11及以后标准中「局部静态变量在第一次调用时初始化,且线程安全」的特性,彻底避免初始化顺序问题:
static std::shared_ptr<JavaClass> getInstance(const std::string& className) { // 局部静态变量,第一次调用时初始化,保证线程安全与初始化顺序 static std::unordered_map<std::string, std::shared_ptr<JavaClass>> m_classes; if (auto it = m_classes.find(className); it != m_classes.end()) { std::cout << "JavaClass returning ref\n"; return it->second; } auto instance = std::make_shared<JavaClass>(className); m_classes.emplace(className, instance); return instance; }
这里额外优化了find+emplace的写法,避免operator[]可能带来的不必要默认构造,提升效率。
方案2:检查构造函数的间接调用
确认JavaClass的构造函数(以及它所依赖的任何成员、函数)中,没有间接调用getInstance。如果构造过程中再次触发getInstance,会在m_classes初始化完成前递归访问它,直接导致访问违规。
方案3:确认JNI环境的初始化时机
确保getInstance的第一次调用是在JNI_OnLoad函数执行完成之后(即JNI环境已完全初始化)。虽然你去掉缓存后代码正常,但如果在JNI环境未就绪时尝试创建全局引用,也可能引发内存异常,需要排查调用时机。
内容的提问来源于stack exchange,提问作者SleepyMode

