• Android OOM问题笔记


    开发中如果内存管理没有做好,那么就会可能出现OOM报错导致应用崩溃。
    我们通过搜索代码中的OOM字样,发现了几个抛出OOM的地方

    创建native线程失败
    原因参考https://blog.csdn.net/aiynmimi/article/details/126991015

    art\runtime\thread.cc

    void Thread::CreateNativeThread(JNIEnv* env, jobject java_peer, size_t stack_size, bool is_daemon) {
    	....................
    	env->SetLongField(java_peer, WellKnownClasses::java_lang_Thread_nativePeer, 0);
      {
        std::string msg(child_jni_env_ext.get() == nullptr ?
            StringPrintf("Could not allocate JNI Env: %s", error_msg.c_str()) :
            StringPrintf("pthread_create (%s stack) failed: %s",
                                     PrettySize(stack_size).c_str(), strerror(pthread_create_result)));
        ScopedObjectAccess soa(env);
        soa.Self()->ThrowOutOfMemoryError(msg.c_str());
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    堆分配内存失败
    art\runtime\heap.cc

    mirror::Object* Heap::AllocateInternalWithGc(Thread* self,
                                                 AllocatorType allocator,
                                                 bool instrumented,
                                                 size_t alloc_size,
                                                 size_t* bytes_allocated,
                                                 size_t* usable_size,
                                                 size_t* bytes_tl_bulk_allocated,
                                                 ObjPtr<mirror::Class>* klass) {
                                                 ..................................
                                                   // If the allocation hasn't succeeded by this point, throw an OOM error.
      if (ptr == nullptr) {
        ScopedAllowThreadSuspension ats;
        ThrowOutOfMemoryError(self, alloc_size, allocator);
      }
      return ptr;
                                                 }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    art\runtime\jni\jni_internal.cc

    该方法出现在EnsureLocalCapacity和PushLocalFrame函数调用中(管理局部引用)

    static jint EnsureLocalCapacityInternal(ScopedObjectAccess& soa, jint desired_capacity,
                                              const char* caller)
          REQUIRES_SHARED(Locks::mutator_lock_) {
        if (desired_capacity < 0) {
          LOG(ERROR) << "Invalid capacity given to " << caller << ": " << desired_capacity;
          return JNI_ERR;
        }
    
        std::string error_msg;
        if (!soa.Env()->locals_.EnsureFreeCapacity(static_cast<size_t>(desired_capacity), &error_msg)) {
          std::string caller_error = android::base::StringPrintf("%s: %s", caller, error_msg.c_str());
          soa.Self()->ThrowOutOfMemoryError(caller_error.c_str());
          return JNI_ERR;
        }
        return JNI_OK;
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    定位到该函数中,从字面意思,理解为申请desired_capacity个内存空间,但是未能申请到足够的空间

    bool IndirectReferenceTable::EnsureFreeCapacity(size_t free_capacity, std::string* error_msg) {
      size_t top_index = segment_state_.top_index;
      if (top_index < max_entries_ && top_index + free_capacity <= max_entries_) {
        return true;
      }
    
      // We're only gonna do a simple best-effort here, ensuring the asked-for capacity at the end.
      if (resizable_ == ResizableCapacity::kNo) {
        *error_msg = "Table is not resizable";
        return false;
      }
    
      // Try to increase the table size.
    
      // Would this overflow?
      if (std::numeric_limits<size_t>::max() - free_capacity < top_index) {
        *error_msg = "Cannot resize table, overflow.";
        return false;
      }
    
      if (!Resize(top_index + free_capacity, error_msg)) {
        LOG(WARNING) << "JNI ERROR: Unable to reserve space in EnsureFreeCapacity (" << free_capacity
                     << "): " << std::endl
                     << MutatorLockedDumpable<IndirectReferenceTable>(*this)
                     << " Resizing failed: " << *error_msg;
        return false;
      }
      return true;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    转Jstring的char*数据过多

     static jstring NewStringUTF(JNIEnv* env, const char* utf) {
     .....................
        size_t utf8_length = strlen(utf);
        if (UNLIKELY(utf8_length > static_cast<uint32_t>(std::numeric_limits<int32_t>::max()))) {
          // Converting the utf8_length to int32_t for String::AllocFromModifiedUtf8() would
          // overflow. Throw OOME eagerly to avoid 2GiB allocation when trying to replace
          // invalid sequences (even if such replacements could reduce the size below 2GiB).
          std::string error =
              android::base::StringPrintf("NewStringUTF input is 2 GiB or more: %zu", utf8_length);
          ScopedObjectAccess soa(env);
          soa.Self()->ThrowOutOfMemoryError(error.c_str());
          return nullptr;
        }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    String
    art\runtime\jni\string-alloc-inl.cc

      const size_t max_length = RoundDown(max_alloc_length, kObjectAlignment / block_size);
      if (UNLIKELY(length > max_length)) {
        self->ThrowOutOfMemoryError(
            android::base::StringPrintf("%s of length %d would overflow",
                                        Class::PrettyDescriptor(string_class).c_str(),
                                        static_cast<int>(length)).c_str());
        return nullptr;
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    Array
    art\runtime\jni\array-alloc-inl.cc

     size_t size = ComputeArraySize(component_count, component_size_shift);
    #ifdef __LP64__
      // 64-bit. No size_t overflow.  
      DCHECK_NE(size, 0U);
    #else
      // 32-bit.
      if (UNLIKELY(size == 0)) {
        self->ThrowOutOfMemoryError(android::base::StringPrintf("%s of length %d would overflow",
                                                                array_class->PrettyDescriptor().c_str(),
                                                                component_count).c_str());
        return nullptr;
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    为什么64bit不会报错呢,我们看到32位有长度限制

    static inline size_t ComputeArraySize(int32_t component_count, size_t component_size_shift) {
      DCHECK_GE(component_count, 0);
    
      size_t component_size = 1U << component_size_shift;
      size_t header_size = Array::DataOffset(component_size).SizeValue();
      size_t data_size = static_cast<size_t>(component_count) << component_size_shift;
      size_t size = header_size + data_size;
    
      // Check for size_t overflow if this was an unreasonable request
      // but let the caller throw OutOfMemoryError.
    #ifdef __LP64__
      // 64-bit. No overflow as component_count is 32-bit and the maximum
      // component size is 8.
      DCHECK_LE((1U << component_size_shift), 8U);
    #else
      // 32-bit.
      DCHECK_NE(header_size, 0U);
      DCHECK_EQ(RoundUp(header_size, component_size), header_size);
      // The array length limit (exclusive).
      const size_t length_limit = (0U - header_size) >> component_size_shift;
      if (UNLIKELY(length_limit <= static_cast<size_t>(component_count))) {
        return 0;  // failure
      }
    #endif
      return size;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    如果规避该问题
    1、内存优化
    2、使用多进程编程
    3、内存泄露排查

  • 相关阅读:
    竞赛无人机搭积木式编程(四)---2023年TI电赛G题空地协同智能消防系统(无人机部分)
    PTE-精听学习(三)
    SpringBoot整合Druid数据源
    Centos7部署Python3环境
    10月12日,每日信息差
    Egg 封装接口返回信息
    [PAT练级笔记] 32 Basic Level 1032 挖掘机技术哪家强
    【企业架构框架】TOGAF 10 现已发布并可用!
    redis配制redis-static-server
    内网对抗-基石框架篇&域树林&域森林架构&信任关系&多域成员层级&信息收集&环境搭建
  • 原文地址:https://blog.csdn.net/weixin_38622593/article/details/133857029