欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

淺談Java中OutOfMemoryError問(wèn)題產(chǎn)生原因

 更新時(shí)間:2023年06月06日 08:34:13   作者:Pika  
本文主要介紹了淺談Java中OutOfMemoryError問(wèn)題產(chǎn)生原因,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

背景

其實(shí)這個(gè)問(wèn)題也挺有趣的,OutOfMemoryError,算是我們常見(jiàn)的一個(gè)錯(cuò)誤了,大大小小的APP,永遠(yuǎn)也逃離不了這個(gè)Error,那么,OutOfMemroyError是不是只有才分配內(nèi)存的時(shí)候才會(huì)發(fā)生呢?是不是只有新建對(duì)象的時(shí)候才會(huì)發(fā)生呢?要弄清楚這個(gè)問(wèn)題,我們就要了解一下這個(gè)Error產(chǎn)生的過(guò)程。

OutOfMemoryError

我們常常在堆棧中看到的OOM日志,大多數(shù)是在java層,其實(shí),真正被設(shè)置OOM的,是在ThrowOutOfMemoryError這個(gè)native方法中

void Thread::ThrowOutOfMemoryError(const char* msg) {
  LOG(WARNING) << "Throwing OutOfMemoryError "
               << '"' << msg << '"'
               << " (VmSize " << GetProcessStatus("VmSize")
               << (tls32_.throwing_OutOfMemoryError ? ", recursive case)" : ")");
  ScopedTrace trace("OutOfMemoryError");
  jni調(diào)用設(shè)置ERROR
  if (!tls32_.throwing_OutOfMemoryError) {
    tls32_.throwing_OutOfMemoryError = true;
    ThrowNewException("Ljava/lang/OutOfMemoryError;", msg);
    tls32_.throwing_OutOfMemoryError = false;
  } else {
    Dump(LOG_STREAM(WARNING));  // The pre-allocated OOME has no stack, so help out and log one.
    SetException(Runtime::Current()->GetPreAllocatedOutOfMemoryErrorWhenThrowingOOME());
  }
}

下面,我們就來(lái)看看,常見(jiàn)的拋出OOM的幾個(gè)路徑

MakeSingleDexFile

在ART中,是支持合成單個(gè)Dex的,它在ClassPreDefine階段,會(huì)嘗試把符合條件的Class(比如非數(shù)據(jù)/私有類)進(jìn)行單Dex生成,這里我們不深入細(xì)節(jié)流程,我們看下,如果此時(shí)把舊數(shù)據(jù)orig_location移動(dòng)到新的final_data數(shù)組里面失敗,就會(huì)觸發(fā)OOM

static std::unique_ptr<const art::DexFile> MakeSingleDexFile(art::Thread* self,
                                                             const char* descriptor,
                                                             const std::string& orig_location,
                                                             jint final_len,
                                                             const unsigned char* final_dex_data)
      REQUIRES_SHARED(art::Locks::mutator_lock_) {
  // Make the mmap
  std::string error_msg;
  art::ArrayRef<const unsigned char> final_data(final_dex_data, final_len);
  art::MemMap map = Redefiner::MoveDataToMemMap(orig_location, final_data, &error_msg);
  if (!map.IsValid()) {
    LOG(WARNING) << "Unable to allocate mmap for redefined dex file! Error was: " << error_msg;
    self->ThrowOutOfMemoryError(StringPrintf(
        "Unable to allocate dex file for transformation of %s", descriptor).c_str());
    return nullptr;
  }  

unsafe創(chuàng)建

我們java層也有一個(gè)很神奇的類,它也能夠操作指針,同時(shí)也能直接創(chuàng)建類對(duì)象,并操控對(duì)象的內(nèi)存指針數(shù)據(jù)嗎,它就是Unsafe,gson里面就大量用到了unsafe去嘗試創(chuàng)建對(duì)象的例子,比如需要?jiǎng)?chuàng)建的對(duì)象沒(méi)有空參數(shù)構(gòu)造函數(shù),這里如果malloc分配內(nèi)存失敗,也會(huì)產(chǎn)生OOM

static jlong Unsafe_allocateMemory(JNIEnv* env, jobject, jlong bytes) {
  ScopedFastNativeObjectAccess soa(env);
  if (bytes == 0) {
    return 0;
  }
  // bytes is nonnegative and fits into size_t
  if (!ValidJniSizeArgument(bytes)) {
    DCHECK(soa.Self()->IsExceptionPending());
    return 0;
  }
  const size_t malloc_bytes = static_cast<size_t>(bytes);
  void* mem = malloc(malloc_bytes);
  if (mem == nullptr) {
    soa.Self()->ThrowOutOfMemoryError("native alloc");
    return 0;
  }
  return reinterpret_cast<uintptr_t>(mem);
}

Thread 創(chuàng)建

其實(shí)我們java層的Thread創(chuàng)建的時(shí)候,都會(huì)走到native的Thread創(chuàng)建,通過(guò)該方法CreateNativeThread,其實(shí)里面就調(diào)用了傳統(tǒng)的pthread_create去創(chuàng)建一個(gè)native Thread,如果創(chuàng)建失?。ū热缣摂M內(nèi)存不足/FD不足),就會(huì)走到代碼塊中,從而產(chǎn)生OOM

void Thread::CreateNativeThread(JNIEnv* env, jobject java_peer, size_t stack_size, bool is_daemon) {
? ?....
? ? if (pthread_create_result == 0) {
? ? ? // pthread_create started the new thread. The child is now responsible for managing the
? ? ? // JNIEnvExt we created.
? ? ? // Note: we can't check for tmp_jni_env == nullptr, as that would require synchronization
? ? ? // ? ? ? between the threads.
? ? ? child_jni_env_ext.release(); ?// NOLINT pthreads API.
? ? ? return;
? ? }
? }
? // Either JNIEnvExt::Create or pthread_create(3) failed, so clean up.
? {
? ? MutexLock mu(self, *Locks::runtime_shutdown_lock_);
? ? runtime->EndThreadBirth();
? }
? // Manually delete the global reference since Thread::Init will not have been run. Make sure
? // nothing can observe both opeer and jpeer set at the same time.
? child_thread->DeleteJPeer(env);
? delete child_thread;
? child_thread = nullptr;
? 如果沒(méi)有return,證明失敗了,爆出OOM
? SetNativePeer(env, java_peer, nullptr);
? {
? ? 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());
? }
}

堆內(nèi)存分配

我們平時(shí)采用new 等方法的時(shí)候,其實(shí)進(jìn)入到ART虛擬機(jī)中,其實(shí)是走到Heap::AllocObjectWithAllocator 這個(gè)方法里面,當(dāng)內(nèi)存分配不足的時(shí)候,就會(huì)發(fā)起一次強(qiáng)有力的gc后再嘗試進(jìn)行內(nèi)存分配,這個(gè)方法就是AllocateInternalWithGc

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) 

流程如下: 

void Heap::ThrowOutOfMemoryError(Thread* self, size_t byte_count, AllocatorType allocator_type) {
? // If we're in a stack overflow, do not create a new exception. It would require running the
? // constructor, which will of course still be in a stack overflow.
? if (self->IsHandlingStackOverflow()) {
? ? self->SetException(
? ? ? ? Runtime::Current()->GetPreAllocatedOutOfMemoryErrorWhenHandlingStackOverflow());
? ? return;
? }??
這里官方給了一個(gè)鉤子
? Runtime::Current()->OutOfMemoryErrorHook();
? 輸出OOM的原因
? std::ostringstream oss;
? size_t total_bytes_free = GetFreeMemory();
? oss << "Failed to allocate a " << byte_count << " byte allocation with " << total_bytes_free
? ? ? << " free bytes and " << PrettySize(GetFreeMemoryUntilOOME()) << " until OOM,"
? ? ? << " target footprint " << target_footprint_.load(std::memory_order_relaxed)
? ? ? << ", growth limit "
? ? ? << growth_limit_;
? // If the allocation failed due to fragmentation, print out the largest continuous allocation.
? if (total_bytes_free >= byte_count) {
? ? space::AllocSpace* space = nullptr;
? ? if (allocator_type == kAllocatorTypeNonMoving) {
? ? ? space = non_moving_space_;
? ? } else if (allocator_type == kAllocatorTypeRosAlloc ||
? ? ? ? ? ? ? ?allocator_type == kAllocatorTypeDlMalloc) {
? ? ? space = main_space_;
? ? } else if (allocator_type == kAllocatorTypeBumpPointer ||
? ? ? ? ? ? ? ?allocator_type == kAllocatorTypeTLAB) {
? ? ? space = bump_pointer_space_;
? ? } else if (allocator_type == kAllocatorTypeRegion ||
? ? ? ? ? ? ? ?allocator_type == kAllocatorTypeRegionTLAB) {
? ? ? space = region_space_;
? ? }
? ? // There is no fragmentation info to log for large-object space.
? ? if (allocator_type != kAllocatorTypeLOS) {
? ? ? CHECK(space != nullptr) << "allocator_type:" << allocator_type
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? << " byte_count:" << byte_count
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? << " total_bytes_free:" << total_bytes_free;
? ? ? // LogFragmentationAllocFailure returns true if byte_count is greater than
? ? ? // the largest free contiguous chunk in the space. Return value false
? ? ? // means that we are throwing OOME because the amount of free heap after
? ? ? // GC is less than kMinFreeHeapAfterGcForAlloc in proportion of the heap-size.
? ? ? // Log an appropriate message in that case.
? ? ? if (!space->LogFragmentationAllocFailure(oss, byte_count)) {
? ? ? ? oss << "; giving up on allocation because <"
? ? ? ? ? ? << kMinFreeHeapAfterGcForAlloc * 100
? ? ? ? ? ? << "% of heap free after GC.";
? ? ? }
? ? }
? }
? self->ThrowOutOfMemoryError(oss.str().c_str());
}

這個(gè)就是我們常見(jiàn)的,也是主要OOM產(chǎn)生的流程

JNI層

這里還有很多,比如JNI層通過(guò)Env調(diào)用NewString等分配內(nèi)存的時(shí)候,會(huì)進(jìn)入條件檢測(cè),比如分配的String長(zhǎng)度超過(guò)最大時(shí)產(chǎn)生Error,即使說(shuō)內(nèi)存空間依舊可以分配,但是超過(guò)了虛擬機(jī)能處理的最大限制,也會(huì)產(chǎn)生OOM

if (UNLIKELY(utf16_length > static_cast<uint32_t>(std::numeric_limits<int32_t>::max()))) {
      // Converting the utf16_length to int32_t would overflow. Explicitly throw an OOME.
      std::string error =
          android::base::StringPrintf("NewStringUTF input has 2^31 or more characters: %zu",
                                      utf16_length);
      ScopedObjectAccess soa(env);
      soa.Self()->ThrowOutOfMemoryError(error.c_str());
      return nullptr;
    }  

OOM 路徑總結(jié)

通過(guò)本文,我們看到了OOM發(fā)生時(shí),可能存在的幾個(gè)主要路徑,其他引起OOM的路徑,也是在這幾個(gè)基礎(chǔ)路徑之上產(chǎn)生的,希望大家以后可以帶著源碼學(xué)習(xí),能夠幫助我們了解ART更深層的秘密。

到此這篇關(guān)于淺談Java中OutOfMemoryError問(wèn)題產(chǎn)生原因的文章就介紹到這了,更多相關(guān)Java OutOfMemoryError內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot讀寫xml上傳到AWS存儲(chǔ)服務(wù)S3的示例

    SpringBoot讀寫xml上傳到AWS存儲(chǔ)服務(wù)S3的示例

    這篇文章主要介紹了SpringBoot讀寫xml上傳到S3的示例,幫助大家更好的理解和使用springboot框架,感興趣的朋友可以了解下
    2020-10-10
  • 深入分析RabbitMQ中死信隊(duì)列與死信交換機(jī)

    深入分析RabbitMQ中死信隊(duì)列與死信交換機(jī)

    這篇文章主要介紹了RabbitMQ中死信隊(duì)列與死信交換機(jī),死信隊(duì)列就是一個(gè)普通的交換機(jī),有些隊(duì)列的消息成為死信后,一般情況下會(huì)被RabbitMQ清理,感興趣想要詳細(xì)了解可以參考下文
    2023-05-05
  • Gradle的SpringBoot項(xiàng)目構(gòu)建圖解

    Gradle的SpringBoot項(xiàng)目構(gòu)建圖解

    這篇文章主要介紹了Gradle的SpringBoot項(xiàng)目構(gòu)建圖解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • SpringBoot整合Mongodb實(shí)現(xiàn)增刪查改的方法

    SpringBoot整合Mongodb實(shí)現(xiàn)增刪查改的方法

    這篇文章主要介紹了SpringBoot整合Mongodb實(shí)現(xiàn)簡(jiǎn)單的增刪查改,MongoDB是一個(gè)以分布式數(shù)據(jù)庫(kù)為核心的數(shù)據(jù)庫(kù),因此高可用性、橫向擴(kuò)展和地理分布是內(nèi)置的,并且易于使用。況且,MongoDB是免費(fèi)的,開(kāi)源的,感興趣的朋友跟隨小編一起看看吧
    2022-05-05
  • Java Structs框架原理案例詳解

    Java Structs框架原理案例詳解

    這篇文章主要介紹了Java Structs框架原理案例詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • Java 反射機(jī)制實(shí)例詳解

    Java 反射機(jī)制實(shí)例詳解

    這篇文章主要介紹了Java 反射機(jī)制實(shí)例詳解的相關(guān)資料,這里對(duì)java中反射機(jī)制進(jìn)行了詳細(xì)的分析,需要的朋友可以參考下
    2017-09-09
  • java實(shí)現(xiàn)簡(jiǎn)易飛機(jī)大戰(zhàn)

    java實(shí)現(xiàn)簡(jiǎn)易飛機(jī)大戰(zhàn)

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)簡(jiǎn)易飛機(jī)大戰(zhàn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • Java對(duì)象初始化過(guò)程代碼塊和構(gòu)造器的調(diào)用順序

    Java對(duì)象初始化過(guò)程代碼塊和構(gòu)造器的調(diào)用順序

    這篇文章主要介紹了Java對(duì)象初始化過(guò)程代碼塊和構(gòu)造器的調(diào)用順序,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-08-08
  • 一文教會(huì)你如何搭建vue+springboot項(xiàng)目

    一文教會(huì)你如何搭建vue+springboot項(xiàng)目

    最近在搗鼓?SpringBoot?與?Vue?整合的項(xiàng)目,所以下面這篇文章主要給大家介紹了關(guān)于如何通過(guò)一篇文章教會(huì)你搭建vue+springboot項(xiàng)目,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-05-05
  • Java開(kāi)發(fā)中讀取XML與properties配置文件的方法

    Java開(kāi)發(fā)中讀取XML與properties配置文件的方法

    這篇文章主要介紹了Java開(kāi)發(fā)中讀取XML與properties配置文件的方法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2017-01-01

最新評(píng)論