最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

背景

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

OutOfMemoryError

我們常常在堆棧中看到的OOM日志,大多數(shù)是在java層,其實(shí),真正被設(shè)置OOM的,是在ThrowOutOfMemoryError這個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());
  }
}

下面,我們就來看看,常見的拋出OOM的幾個路徑

MakeSingleDexFile

在ART中,是支持合成單個Dex的,它在ClassPreDefine階段,會嘗試把符合條件的Class(比如非數(shù)據(jù)/私有類)進(jìn)行單Dex生成,這里我們不深入細(xì)節(jié)流程,我們看下,如果此時把舊數(shù)據(jù)orig_location移動到新的final_data數(shù)組里面失敗,就會觸發(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層也有一個很神奇的類,它也能夠操作指針,同時也能直接創(chuàng)建類對象,并操控對象的內(nèi)存指針數(shù)據(jù)嗎,它就是Unsafe,gson里面就大量用到了unsafe去嘗試創(chuàng)建對象的例子,比如需要創(chuàng)建的對象沒有空參數(shù)構(gòu)造函數(shù),這里如果malloc分配內(nèi)存失敗,也會產(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)建的時候,都會走到native的Thread創(chuàng)建,通過該方法CreateNativeThread,其實(shí)里面就調(diào)用了傳統(tǒng)的pthread_create去創(chuàng)建一個native Thread,如果創(chuàng)建失敗(比如虛擬內(nèi)存不足/FD不足),就會走到代碼塊中,從而產(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;
? 如果沒有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)存分配

我們平時采用new 等方法的時候,其實(shí)進(jìn)入到ART虛擬機(jī)中,其實(shí)是走到Heap::AllocObjectWithAllocator 這個方法里面,當(dāng)內(nèi)存分配不足的時候,就會發(fā)起一次強(qiáng)有力的gc后再嘗試進(jìn)行內(nèi)存分配,這個方法就是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;
? }??
這里官方給了一個鉤子
? 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());
}

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

JNI層

這里還有很多,比如JNI層通過Env調(diào)用NewString等分配內(nèi)存的時候,會進(jìn)入條件檢測,比如分配的String長度超過最大時產(chǎn)生Error,即使說內(nèi)存空間依舊可以分配,但是超過了虛擬機(jī)能處理的最大限制,也會產(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é)

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

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

相關(guān)文章

  • Java使用線程池實(shí)現(xiàn)socket編程的方法詳解

    Java使用線程池實(shí)現(xiàn)socket編程的方法詳解

    這篇文章主要為大家詳細(xì)介紹了Java使用線程池實(shí)現(xiàn)socket編程的方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • Jenkins 關(guān)閉和重啟詳細(xì)介紹及實(shí)現(xiàn)

    Jenkins 關(guān)閉和重啟詳細(xì)介紹及實(shí)現(xiàn)

    這篇文章主要介紹了Jenkins的關(guān)閉、重啟的相關(guān)資料,用jar -jar jenkins.war來啟動jenkins服務(wù)器,那么我們?nèi)绾侮P(guān)閉或者重啟jenkins服務(wù)器呢,這里就給出實(shí)現(xiàn)的方法,需要的朋友可以參考下
    2016-11-11
  • 妙用Java8中的Function接口消滅if...else

    妙用Java8中的Function接口消滅if...else

    在開發(fā)過程中經(jīng)常會使用if...else...進(jìn)行判斷拋出異常、分支處理等操作。這些if...else...充斥在代碼中嚴(yán)重影響了代碼代碼的美觀,本文就妙用Java8中的Function接口消滅if...else,感興趣的可以了解一下
    2022-01-01
  • Spring security如何重寫Filter實(shí)現(xiàn)json登錄

    Spring security如何重寫Filter實(shí)現(xiàn)json登錄

    這篇文章主要介紹了Spring security 如何重寫Filter實(shí)現(xiàn)json登錄,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • SpringBoot整合Keycloak的項(xiàng)目實(shí)踐

    SpringBoot整合Keycloak的項(xiàng)目實(shí)踐

    本文介紹了如何設(shè)置了Keycloak 服務(wù)器及在SpringBoot中使用 Spring Security OAuth2.0結(jié)合Keycloak實(shí)現(xiàn)認(rèn)證和授權(quán),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2026-01-01
  • Java BeanUtils.copyProperties的詳解

    Java BeanUtils.copyProperties的詳解

    這篇文章主要介紹了Java BeanUtils.copyProperties的詳解,本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • Mybatis增強(qiáng)版MyBatis-Flex的具體使用

    Mybatis增強(qiáng)版MyBatis-Flex的具體使用

    Mybatis-Flex一個用于增強(qiáng)MyBatis的框架,本文主要介紹了Mybatis增強(qiáng)版MyBatis-Flex的具體使用,具有一定的參考價值,感興趣的可以了解一下
    2024-06-06
  • java Random.nextInt()方法的具體使用

    java Random.nextInt()方法的具體使用

    這篇文章主要介紹了java Random.nextInt()方法的具體使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • java中的instanceof關(guān)鍵字詳細(xì)解讀

    java中的instanceof關(guān)鍵字詳細(xì)解讀

    這篇文章主要介紹了java中的instanceof關(guān)鍵字詳細(xì)解讀,instanceof 是 Java 的保留關(guān)鍵字,它的作用是測試它左邊的對象是否是它右邊的類的實(shí)例,返回 boolean 的數(shù)據(jù)類型,需要的朋友可以參考下
    2024-01-01
  • 淺談Java8 判空新寫法

    淺談Java8 判空新寫法

    在開發(fā)過程中很多時候會遇到判空校驗(yàn),如果不做判空校驗(yàn)則會產(chǎn)生NullPointerException異常,本文就來介紹一下Java8 判空新寫法,感興趣的可以了解一下
    2021-09-09

最新評論

望江县| 府谷县| 乐平市| 通山县| 大港区| 汝阳县| 日土县| 乌拉特中旗| 措美县| 东山县| 凯里市| 桃园市| 浙江省| 响水县| 泸溪县| 遵义市| 平塘县| 苗栗县| 囊谦县| 通辽市| 沧源| 无锡市| 秭归县| 东莞市| 蕉岭县| 南乐县| 贺州市| 虞城县| 淳安县| 昌吉市| 台南县| 团风县| 永和县| 普兰县| 尉氏县| 布尔津县| 阿鲁科尔沁旗| 正阳县| 德惠市| 高陵县| 石城县|