@@ -9,7 +9,7 @@ OOM(OutOfMemoryError),最近线上版本出现了大量线程OOM的crash,尤
99
1010#### [ XXXClassName] of length XXX would overflow“是系统限制String/Array的长度所致,这种情况比较少。
1111
12- #### java.lang.OutOfMemoryError: Failed to allocate a XXX byte allocation with XXX free bytes and XXXKB until OOM
12+ #### java.lang.OutOfMemoryError: " Failed to allocate a " << byte_count << " byte allocation with " << total_bytes_free << " free bytes and " << PrettySize(GetFreeMemoryUntilOOME()) << " until OOM";
1313
1414通常情况下是因为` java ` 堆内存不足导致的,即` Runtime.getRuntime().maxMemory() ` 获取到的最大内存无法满足要申请的内存大小时,这种情况比较好模拟,例如我们可以通过` new byte[] ` 的方式来申请超过` maxMemory() ` 的内存,但是也有一些情况是堆内存充裕,而且设备内存也充裕的情况下发生的。
1515#### java.lang.OutOfMemoryError: Could not allocate JNI Env(代号JNIEnv)
@@ -20,15 +20,15 @@ Limit Soft Limit Hard Limit Units
2020Max cpu time unlimited unlimited seconds
2121Max file size unlimited unlimited bytes
2222Max data size unlimited unlimited bytes
23- Max stack size 8388608 unlimited bytes
23+ Max stack size 8388608 unlimited bytes // 整个系统的
2424Max core file size 0 unlimited bytes
2525Max resident set unlimited unlimited bytes
26- Max processes 17235 17235 processes
27- Max open files 32768 32768 files
28- Max locked memory 67108864 67108864 bytes
26+ Max processes 17235 17235 processes // 整个系统的最大进程数,底层只有进程,线程也是通过进程实现的
27+ Max open files 32768 32768 files // 每个进程最大打开文件的数量
28+ Max locked memory 67108864 67108864 bytes // 线程创建过程中分配线程私有stack使用的mmap调用没有设置MAP_LOCKED,所以这个限制与线程创建过程无关
2929Max address space unlimited unlimited bytes
3030Max file locks unlimited unlimited locks
31- Max pending signals 17235 17235 signals
31+ Max pending signals 17235 17235 signals // c层信号个数阈值,与线程创建过程无关
3232Max msgqueue size 819200 819200 bytes
3333Max nice priority 40 40
3434Max realtime priority 0 0
@@ -212,6 +212,7 @@ void Thread::CreateNativeThread(JNIEnv* env, jobject java_peer, size_t stack_siz
212212 {
213213 std:: string msg(child_jni_env_ext. get() == nullptr ?
214214 StringPrintf(" Could not allocate JNI Env: %s" , error_msg. c_str()) :
215+ // 具体的错误信息由pthread_create_result的返回的错误码给出。
215216 StringPrintf(" pthread_create (%s stack) failed: %s" ,
216217 PrettySize(stack_size). c_str(), strerror(pthread_create_result)));
217218 ScopedObjectAccess soa(env);
@@ -338,7 +339,7 @@ IndirectReferenceTable::IndirectReferenceTable(size_t max_count,
338339 last_known_previous_state_ = kIRTFirstSegment;
339340}
340341```
341- 如果上面失败的话,那就只有一种情况就是 MemMap :: MapAnonymous 失败了,我们继续看[MemMap ](https: // android.googlesource.com/platform/art/+/refs/tags/android-9.0.0_r41/runtime/mem_map.cc)
342+ 如果上面失败的话,那就只有一种情况就是 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)
342343```
343344MemMap * MemMap :: MapAnonymous(const char * name,
344345 uint8_t* expected_ptr,
@@ -383,6 +384,7 @@ MemMap* MemMap::MapAnonymous(const char* name,
383384 debug_friendly_name += name;
384385 // 1. 创建
385386 fd. reset(ashmem_create_region(debug_friendly_name. c_str(), page_aligned_byte_count));
387+ // == -1 就说明是fd超过了系统限制的最大fd量,错误信息中会有Too many open files的提示
386388 if (fd. get() == - 1 ) {
387389 // We failed to create the ashmem region. Print a warning, but continue
388390 // anyway by creating a true anonymous mmap with an fd of -1. It is
@@ -429,6 +431,40 @@ MemMap* MemMap::MapAnonymous(const char* name,
429431 page_aligned_byte_count, prot, reuse);
430432}
431433```
434+ 这里面又用到了`ashmem_create_region()`方法,该方法的作用就是创建一块ashmen匿名共享内存,并返回一个文件描述符,我们看一下[ashmem_create_region](https: // android.googlesource.com/platform/system/core/+/4f6e8d7a00cbeda1e70cc15be9c4af1018bdad53/libcutils/ashmem-dev.c)的源码:
435+ ```
436+ /*
437+ * ashmem_create_region - creates a new ashmem region and returns the file
438+ * descriptor, or <0 on error
439+ *
440+ * `name' is an optional label to give the region (visible in /proc/pid/maps)
441+ * `size' is the size of the region, in page-aligned bytes
442+ */
443+ int ashmem_create_region(const char * name, size_t size)
444+ {
445+ int fd, ret;
446+ // 打开一个fd
447+ fd = open(ASHMEM_DEVICE , O_RDWR );
448+ if (fd < 0 )
449+ return fd;
450+ if (name) {
451+ char buf[ASHMEM_NAME_LEN ];
452+ strlcpy(buf, name, sizeof(buf));
453+ ret = ioctl(fd, ASHMEM_SET_NAME , buf);
454+ if (ret < 0 )
455+ goto error;
456+ }
457+ ret = ioctl(fd, ASHMEM_SET_SIZE , size);
458+ if (ret < 0 )
459+ goto error;
460+ return fd;
461+ error:
462+ close(fd);
463+ return ret;
464+ }
465+ ```
466+
467+
432468上面的两个步骤中,不论第一个步骤执行成功与否,都会执行第二步,但是执行的行为不同
433469
434470- 如果第一步执行成功,就会通过Andorid 的匿名共享内存(Anonymous Shared Memory )分配4KB(一个page)内核态内存,然后再通过Linux 的mmap调用映射到用户态虚拟内存地址空间。
0 commit comments