1-Intro
背景:
- 使用了 三方的
SDK, 这个SDK使用了JNI集成了 豆包的C++SDK - 三方团队封装的通过
Attach线程回调
2-Actions
用 MicroMeter 同时观察了内存和线程.
线程 的状态:
NEW: 初始的状态 ;RUNNABLE: 可被调度的状态, 正在CPU上运行或者 正在等待CPU的时间片 (ready状态) ;BLOCKED: 线程正在等待 监视器的锁monitor lock, 常见于等待进入synchronized块/方法 ;WAITING: 线程无限等待另一个线程执行操作. 例如:Object.wait()Thread.join()LockSupport.park()
TIMED_WAITING: 现成等待另一个线程的执行操作, 但是有最大的等待时间.Thread.sleep(timeout)Object.wait(timeout)Thread.join(timeout)LockSupport.parkNanos()LockSupport.parkUntil()
TERMINATED: 线程已经执行完毕
1)-Waiting 线程很多, runnable 反而不多
thread --state runnable
thread --state waiting2)-从 ZGC 改为 G1 后. gc 可以释放内存
vmtool --action forceGc3)-同时分析 —live 和 没有 live
heapdump --live /home/aitogether/ysz/dump.hprof4)-分析 heapdump 文件
--live: 代表存活的对象
3-JNI 可能的问题
1)-JNI 有三种引用类型
JNI 中有三种引用类型:
- 局部引用(
Local References) :- 自动释放,
native方法返回的时候释放 - 只在创建他的线程中有效
- 阻止对象被
GC回收 - 默认最少支持 16 个局部引用
- 自动释放,
- 全局引用 (
Global References)- 需要手动释放
- 可以跨方法和线程使用
- 阻止对象被
GC回收 - 通过
NewGlobalRef创建
- 弱全局引用 (
Weak Global Reference)- 需要手动释放
- 可以跨方法和线程使用
- 不阻止对象被
GC回收 - 通过
NewWeakGlobalRef创建
2)-资源泄露的 DEMO
1. 局部引用的泄露
// 错误示例 - 循环中未释放局部引用
for (i = 0; i < len; i++) {
jstring str = (*env)->GetObjectArrayElement(env, arr, i);
// 使用str
// 未调用DeleteLocalRef
}
2. 全局引用的泄露
// 错误示例 - 创建全局引用后未释放
static jclass cls = NULL;
if (cls == NULL) {
jclass localCls = (*env)->FindClass(env, "java/lang/String");
cls = (*env)->NewGlobalRef(env, localCls);
// 未在适当时机调用DeleteGlobalRef
}3)-最佳实践
1. 局部引用的管理
// 推荐使用PushLocalFrame/PopLocalFrame
if ((*env)->PushLocalFrame(env, 10) < 0) {
return NULL; // 内存不足
}
// 执行操作
result = (*env)->PopLocalFrame(env, result);2. 确保引用的容量
if ((*env)->EnsureLocalCapacity(env, expectedRefs) < 0) {
return NULL; // 内存不足
}3. 及时释放不需要的引用
// 使用完立即释放
(*env)->DeleteLocalRef(env, obj);