本地方法栈不维护Java内存变量,仅提供C/C++执行栈帧;JNI需分三类管理:基本类型传值无需维护,对象引用须显式DeleteLocalRef防溢出,本地内存须配对释放;所有跨语言访问必须经JNIEnv函数。

本地方法栈本身不维护 Java 层的内存变量,它只负责为 C/C++ 本地代码提供执行所需的栈帧空间。所谓“维护 Native 方法调用时的内存变量”,实质是厘清三类变量的归属与生命周期,并通过正确使用 JNI 接口来避免泄漏、崩溃或 GC 干扰。
Java 基本类型变量:传值不传引用,无需手动维护
int、boolean、long 等基本类型在 JNI 调用中按值传递。C/C++ 函数内修改它们,不会影响 Java 层原始变量。
- Java 层传入:
jint x = 100;→ native 方法接收jint x - C/C++ 中可任意修改
x,但 Java 栈上原来的局部变量不受影响 - 无需释放、无需缓存、无需跟踪——它们是纯副本
Java 对象与数组:引用需显式管理,避免局部引用膨胀
jobject、jstring、jintArray 等不是指针,而是 JVM 内部句柄。它们在 JNIEnv 的局部引用表中注册,超出作用域不自动清理,容易撑爆表(默认上限常为 512)。
- 每次调用
NewObject、NewStringUTF、NewIntArray都会新增一个局部引用 - 大量创建后未及时删除,会导致
JNI local reference overflow或卡顿 - 用
env->DeleteLocalRef(obj)主动释放不再需要的引用 - 循环中创建对象时,务必在每次迭代末尾 delete,或改用全局引用(
NewGlobalRef)+ 显式DeleteGlobalRef
本地分配的内存:完全脱离 JVM GC,必须配对释放
C/C++ 中用 malloc、new、aligned_alloc 分配的内存,JVM 不知情、不回收、不扫描。
- 常见场景:native 层读取文件到缓冲区、图像解码输出像素数组、加密计算中间态
- 必须在 Java 层提供配套的
freeXXX()native 方法,并由 Cleaner 或 PhantomReference 触发 - 避免在
finalize()中释放——已被弃用且不可靠 - 高并发下注意 malloc 分配器行为:ptmalloc2 易碎片化,可 LD_PRELOAD jemalloc 改善 RSS 增长
JNIEnv 是边界守门人,不是透明管道
所有跨语言数据访问都必须经由 JNIEnv 提供的函数完成,不能直接 cast、解引用或 memcpy 假设的内存布局。
-
jstring不能当const char*直接用,须调用GetStringUTFChars+ReleaseStringUTFChars -
jbyteArray不能直接当uint8_t*访问,要用GetByteArrayElements或更安全的GetByteArrayRegion - 访问 Java 对象字段必须用
GetObjectField、GetIntField等,不能靠偏移硬算 - 每个线程的
JNIEnv*是私有结构体指针,不可跨线程复用











