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());
  }
}

堆分配内存失败
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;
                                             }

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;
  }

定位到该函数中,从字面意思,理解为申请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;
}

转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;
    }
    }

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;
  }

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;
  }

为什么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、内存泄露排查

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值