@@ -280,18 +280,7 @@ bool JNIEnvExt::CheckLocalsValid(JNIEnvExt* in) NO_THREAD_SAFETY_ANALYSIS {
280280 return in- > locals_. IsValid ();
281281}
282282```
283- 所以根本原因是因为 JNIEnvExt :: table_override_ 仍然为nullptr导致的返回false
284- 而它的赋值在GetFunctionTable 方法中
285- ```
286- const JNINativeInterface * JNIEnvExt :: GetFunctionTable(bool check_jni) {
287- const JNINativeInterface * override = JNIEnvExt :: table_override_;
288- if (override != nullptr) {
289- return override;
290- }
291- return check_jni ? GetCheckJniNativeInterface() : GetJniNativeInterface();
292- }
293- ```
294- GetFunctionTable 方法又被构造函数调用
283+ 从代码上看,基本排除是传入的参数nullptr导致的,所以根本原因是locals. IsValid 返回了false ,而locals是JNIEnvExt 的一个成员变量,在JNIEnvExt 构造的时候通过成员列表方式初始化
295284```
296285JNIEnvExt :: JNIEnvExt(Thread * self_in, JavaVMExt * vm_in, std:: string* error_msg)
297286 : self_(self_in),
@@ -308,7 +297,15 @@ JNIEnvExt::JNIEnvExt(Thread* self_in, JavaVMExt* vm_in, std::string* error_msg)
308297 unchecked_functions_ = GetJniNativeInterface();
309298}
310299```
311- 构造函数中又使用了[IndirectReferenceTable ](https: // android.googlesource.com/platform/art/+/refs/tags/android-9.0.0_r41/runtime/indirect_reference_table.cc)类,他的构造函数为
300+
301+ 而[locals_. isValid](https: // android.googlesource.com/platform/art/+/refs/tags/android-9.0.0_r41/runtime/indirect_reference_table.cc)的方法的源码为:
302+ ```
303+ bool IndirectReferenceTable :: IsValid() const {
304+ return table_mem_map_. get() != nullptr;
305+ }
306+ ```
307+ 所以只可能是table_men_map为nullptr导致的。
308+ 而[IndirectReferenceTable ](https: // android.googlesource.com/platform/art/+/refs/tags/android-9.0.0_r41/runtime/indirect_reference_table.cc)类,他的构造函数为
312309```
313310IndirectReferenceTable :: IndirectReferenceTable(size_t max_count,
314311 IndirectRefKind desired_kind,
@@ -339,6 +336,8 @@ IndirectReferenceTable::IndirectReferenceTable(size_t max_count,
339336 last_known_previous_state_ = kIRTFirstSegment;
340337}
341338```
339+
340+
342341如果上面失败的话,那就只有一种情况就是 MemMap :: MapAnonymous 失败了,而MemMap :: MapAnonymous 的作用是为JNIEnv 结构体中的Indirect_Reference_table(C 层用于存储JNI 局部/ 全局变量)申请内存,我们继续看[MemMap ](https: // android.googlesource.com/platform/art/+/refs/tags/android-9.0.0_r41/runtime/mem_map.cc)
343342```
344343MemMap * MemMap :: MapAnonymous(const char * name,
@@ -471,7 +470,7 @@ error:
471470- 如果第一步执行失败,第二步就会通过Linux 的mmap调用创建一段虚拟内存。
472471
473472而上面失败的情况主要有:
474- - 第一步失败的情况一般是内核分配内存失败,这种情况下,整个设备OS 的内存应该都处于非常紧张的状态。
473+ - 第一步失败的情况一般是内核分配内存失败,这种情况下,整个设备OS 的内存应该都处于非常紧张的状态。但是我们从crash的信息里面看用户的内存还是挺充足的,所以排除这种情况。
475474- 第二步失败的情况一般是进程虚拟内存地址空间耗尽。而且会打印Failed anonymous mmap的错误
476475
477476所以这里child_jni_env_ext. get() == nullptr 通常是因为第二步失败,也就是进程虚拟内存地址空间耗尽。所以这就是代号JNIEnv OOM 的原因。
@@ -779,6 +778,8 @@ Thread.UncaughtExceptionHandler捕获到OutOfMemoryError时记录/proc/pid目录
779778
780779
781780说面分析了那么多,结论就是因为虚拟内存空间耗尽导致的,但是究竟什么情况才会出现耗尽的情况?
781+ Android系统给每个进程分配了一定的虚拟地址空间大小,进程使用的虚拟空间如果超过阈值,就会触发OOM。所以只可能是线程太多,消耗了大部分虚拟内存地址空间,从而引发了当前进程空间不足。
782+
782783[Virtual Memory and Linux](https://events.linuxfoundation.org/sites/events/files/slides/elc_2016_mem.pdf)
783784[Android进程的内存管理分析](https://blog.csdn.net/gemmem/article/details/8920039)
784785
0 commit comments