<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                [原文出處----------------ART運行時Compacting GC堆創建過程分析](http://blog.csdn.net/luoshengyang/article/details/44789295) 引進了Compacting GC之后,ART運行時的堆空間結構就發生了變化。這是由于Compacting GC和Mark-Sweep GC的算法不同,要求底層的堆具有不同的空間結構。同時,即使是原來的Mark-Sweep GC,由于需要支持新的同構空間壓縮特性(Homogeneous Space Compact),也使得它們要具有與原來不一樣的堆空間結構。本文就對這些堆空間創建過程進行詳細的分析。 從前面[ART運行時Java堆創建過程分析](http://blog.csdn.net/luoshengyang/article/details/42379729)一文可以知道,在沒有Compacting GC之前,Mark-Sweep GC的堆由Image Space、Zygote Space、Allocation Space和Large Object Space四種Space組成。其中,Allocation Space是從Zygote Space中分離出來的,它們都是一種DlMallocSpace。引入Compacting GC之后,Image Space和Large Object Space沒有發生根本性的變化,但是Zygote Space和Allocation Space就發生了很大的變化。因此,接下來我們就結合Compacting GC以及其它的一些新特性來分析Zygote Space和Allocation Space都發生了哪些變化。 從前面[ART運行時Compacting GC簡要介紹和學習計劃](http://blog.csdn.net/luoshengyang/article/details/44513977)一文可以知道,用來分配對象的空間可以是一種DlMallocSpace,也可以是一種RosAllocSpace,因此,堆空間發生的第一個變化是用來分配對象的空間有可能是一個DlMallocSpace,也有可能是一個RosAllocSpace。 從前面[ART運行時Compacting GC簡要介紹和學習計劃](http://blog.csdn.net/luoshengyang/article/details/44513977)一文還可以知道,Semi-Space GC需要兩個Bump Pointer Space,Generational Semi-Space GC需要兩個Bump Pointer Space和一個Promote Space,Mark-Compact GC需要一個Bump Pointer Space。因此,我們需要增加一種類型為Bump Pointer的Space,以及一個Promote Space。 此外,我們還需要一個Non-Moving Space。由于在Compacting GC中,涉及到對象的移動,但是有些對象,例如類對象(Class)、類方法對象(ArtMethod)和類成員變量對象(ArtField),它們一經加載后,基本上就會一直存在。因此,頻繁對此類對象進行移動是無益的,我們需要將它們分配在一個不能移動的Space中,以減少在Compacting GC需要處理的對象的數量。 所謂的同構空間壓縮特性(Homogeneous Space Compact),是針對Mark-Sweep GC而言的。一個Space需要有Main和Backup之分。執行同構空間壓縮時,將Main Space的對象移動至Backup Space中去,再將Main Space和Backup Space進行交換,這樣就達到壓縮空間,即減少內存碎片的作用。 綜合前面的分析,我們就列出ART運行時支持的各種GC的堆空間結構,如下三個圖所示: ![](https://box.kancloud.cn/86122b680dfd58836c3d8e8d56b14357_521x275.png) 圖1 Mark-Sweep GC的堆空間結構 ![](https://box.kancloud.cn/4af9e9a43e6a6fc2bfdae46db92b802b_522x251.png) 圖2 Semi-Space GC和Mark-Compact GC的堆空間結構 ![](https://box.kancloud.cn/9cc0605dc49752a10483f44fdcf3e2a0_609x288.png) 圖3 Generational Semi-Space GC的堆空間結構 接下來,我們將結構源代碼來詳細分析上述三個圖各個Space的創建過程,這樣就可以更好理解這三個圖所表達的意思。 從前面[ART運行時Java堆創建過程分析](http://blog.csdn.net/luoshengyang/article/details/42379729)一文可以知道,堆的創建是從在ART運行時內部創建一個Heap對象開始的,如下所示: ~~~ bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized) { ...... heap_ = new gc::Heap(options->heap_initial_size_, options->heap_growth_limit_, options->heap_min_free_, options->heap_max_free_, options->heap_target_utilization_, options->foreground_heap_growth_multiplier_, options->heap_maximum_size_, options->heap_non_moving_space_capacity_, options->image_, options->image_isa_, options->collector_type_, options->background_collector_type_, options->parallel_gc_threads_, options->conc_gc_threads_, options->low_memory_mode_, options->long_pause_log_threshold_, options->long_gc_log_threshold_, options->ignore_max_footprint_, options->use_tlab_, options->verify_pre_gc_heap_, options->verify_pre_sweeping_heap_, options->verify_post_gc_heap_, options->verify_pre_gc_rosalloc_, options->verify_pre_sweeping_rosalloc_, options->verify_post_gc_rosalloc_, options->use_homogeneous_space_compaction_for_oom_, options->min_interval_homogeneous_space_compaction_by_oom_); ...... } ~~~ 這個函數定義在文件art/runtime/runtime.cc中。 創建堆所需要的一般性參數的含義可以參考前面ART運行時Java堆創建過程分析一文,這里我們只解釋幾個與Compacting GC相關的參數: * **options->heap_non_moving_space_capacity_**:Non-Moving Space的大小,可以通過ART運行時啟動選項-XX:NonMovingSpaceCapacity來指定,默認大小為kDefaultNonMovingSpaceCapacity(64MB)。 * **options->collector_type_**:Foreground GC的類型,可以通過ART運行時啟動選項-Xgc指定。如果沒有指定,在編譯ART運行時時,可以通過ART_DEFAULT_GC_TYPE_IS_CMS、ART_DEFAULT_GC_TYPE_IS_SS和ART_DEFAULT_GC_TYPE_IS_GSS這三個宏分別默認為Concurrent Mark-Sweep GC、Semi-Space GC或者Generational Semi-Space GC。 * **options->background_collector_type_**:Background GC的類型,可以通過ART運行時啟動選項-XX:BackgroundGC指定。如果沒有指定,在編譯ART運行時時,可以通過ART_USE_HSPACE_COMPACT宏指定為Homogeneous-Space-Compact。如果沒有指定ART_USE_HSPACE_COMPACT宏,默認就與Foreground GC一樣。 * **options->use_homogeneous_space_compaction_for_oom_**:是否在OOM時執行Homogeneous-Space-Compact,可以通過ART運行時啟動選項-XX:EnableHSpaceCompactForOOM和-XX:DisableHSpaceCompactForOOM來設置為支持和不支持。如果沒有指定,默認不支持。 * **options->min_interval_homogeneous_space_compaction_by_oom_**:OOM時執行Homogeneous-Space-Compact的最小時間間隔,可以在OOM時頻繁地執行Homogeneous-Space-Compact,固定為100秒。 Heap對象的創建和初始化過程如下所示: ~~~ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max_free, double target_utilization, double foreground_heap_growth_multiplier, size_t capacity, size_t non_moving_space_capacity, const std::string& image_file_name, const InstructionSet image_instruction_set, CollectorType foreground_collector_type, CollectorType background_collector_type, size_t parallel_gc_threads, size_t conc_gc_threads, bool low_memory_mode, size_t long_pause_log_threshold, size_t long_gc_log_threshold, bool ignore_max_footprint, bool use_tlab, bool verify_pre_gc_heap, bool verify_pre_sweeping_heap, bool verify_post_gc_heap, bool verify_pre_gc_rosalloc, bool verify_pre_sweeping_rosalloc, bool verify_post_gc_rosalloc, bool use_homogeneous_space_compaction_for_oom, uint64_t min_interval_homogeneous_space_compaction_by_oom) ...... byte* requested_alloc_space_begin = nullptr; if (!image_file_name.empty()) { std::string error_msg; space::ImageSpace* image_space = space::ImageSpace::Create(image_file_name.c_str(), image_instruction_set, &error_msg); if (image_space != nullptr) { AddSpace(image_space); // Oat files referenced by image files immediately follow them in memory, ensure alloc space // isn't going to get in the middle byte* oat_file_end_addr = image_space->GetImageHeader().GetOatFileEnd(); ...... requested_alloc_space_begin = AlignUp(oat_file_end_addr, kPageSize); } ...... } ...... bool support_homogeneous_space_compaction = background_collector_type_ == gc::kCollectorTypeHomogeneousSpaceCompact || use_homogeneous_space_compaction_for_oom; // We may use the same space the main space for the non moving space if we don't need to compact // from the main space. // This is not the case if we support homogeneous compaction or have a moving background // collector type. bool separate_non_moving_space = is_zygote || support_homogeneous_space_compaction || IsMovingGc(foreground_collector_type_) || IsMovingGc(background_collector_type_); if (foreground_collector_type == kCollectorTypeGSS) { separate_non_moving_space = false; } std::unique_ptr<MemMap> main_mem_map_1; std::unique_ptr<MemMap> main_mem_map_2; byte* request_begin = requested_alloc_space_begin; if (request_begin != nullptr && separate_non_moving_space) { request_begin += non_moving_space_capacity; } ...... std::unique_ptr<MemMap> non_moving_space_mem_map; if (separate_non_moving_space) { // Reserve the non moving mem map before the other two since it needs to be at a specific // address. non_moving_space_mem_map.reset( MemMap::MapAnonymous("non moving space", requested_alloc_space_begin, non_moving_space_capacity, PROT_READ | PROT_WRITE, true, &error_str)); ...... // Try to reserve virtual memory at a lower address if we have a separate non moving space. request_begin = reinterpret_cast<byte*>(300 * MB); } // Attempt to create 2 mem maps at or after the requested begin. main_mem_map_1.reset(MapAnonymousPreferredAddress(kMemMapSpaceName[0], request_begin, capacity_, PROT_READ | PROT_WRITE, &error_str)); ...... if (support_homogeneous_space_compaction || background_collector_type_ == kCollectorTypeSS || foreground_collector_type_ == kCollectorTypeSS) { main_mem_map_2.reset(MapAnonymousPreferredAddress(kMemMapSpaceName[1], main_mem_map_1->End(), capacity_, PROT_READ | PROT_WRITE, &error_str)); ...... } // Create the non moving space first so that bitmaps don't take up the address range. if (separate_non_moving_space) { // Non moving space is always dlmalloc since we currently don't have support for multiple // active rosalloc spaces. const size_t size = non_moving_space_mem_map->Size(); non_moving_space_ = space::DlMallocSpace::CreateFromMemMap( non_moving_space_mem_map.release(), "zygote / non moving space", kDefaultStartingSize, initial_size, size, size, false); non_moving_space_->SetFootprintLimit(non_moving_space_->Capacity()); ...... AddSpace(non_moving_space_); } // Create other spaces based on whether or not we have a moving GC. if (IsMovingGc(foreground_collector_type_) && foreground_collector_type_ != kCollectorTypeGSS) { // Create bump pointer spaces. // We only to create the bump pointer if the foreground collector is a compacting GC. // TODO: Place bump-pointer spaces somewhere to minimize size of card table. bump_pointer_space_ = space::BumpPointerSpace::CreateFromMemMap("Bump pointer space 1", main_mem_map_1.release()); ...... AddSpace(bump_pointer_space_); temp_space_ = space::BumpPointerSpace::CreateFromMemMap("Bump pointer space 2", main_mem_map_2.release()); ...... AddSpace(temp_space_); ...... } else { CreateMainMallocSpace(main_mem_map_1.release(), initial_size, growth_limit_, capacity_); ...... AddSpace(main_space_); if (!separate_non_moving_space) { non_moving_space_ = main_space_; ...... } if (foreground_collector_type_ == kCollectorTypeGSS) { ...... // Create bump pointer spaces instead of a backup space. main_mem_map_2.release(); bump_pointer_space_ = space::BumpPointerSpace::Create("Bump pointer space 1", kGSSBumpPointerSpaceCapacity, nullptr); ...... AddSpace(bump_pointer_space_); temp_space_ = space::BumpPointerSpace::Create("Bump pointer space 2", kGSSBumpPointerSpaceCapacity, nullptr); ...... AddSpace(temp_space_); } else if (main_mem_map_2.get() != nullptr) { const char* name = kUseRosAlloc ? kRosAllocSpaceName[1] : kDlMallocSpaceName[1]; main_space_backup_.reset(CreateMallocSpaceFromMemMap(main_mem_map_2.release(), initial_size, growth_limit_, capacity_, name, true)); ...... // Add the space so its accounted for in the heap_begin and heap_end. AddSpace(main_space_backup_.get()); } } ...... } ~~~ 這個函數定義在文件art/runtime/gc/heap.cc中。 由于底層堆的空間結構要兼顧到上層的各種GC,因此堆創建過程中涉及到邏輯是比較復雜的,我們將上述函數涉及到的代碼分段來解讀。 第一段代碼是關于Image Space的創建的,如下所示: ~~~ byte* requested_alloc_space_begin = nullptr; if (!image_file_name.empty()) { std::string error_msg; space::ImageSpace* image_space = space::ImageSpace::Create(image_file_name.c_str(), image_instruction_set, &error_msg); if (image_space != nullptr) { AddSpace(image_space); // Oat files referenced by image files immediately follow them in memory, ensure alloc space // isn't going to get in the middle byte* oat_file_end_addr = image_space->GetImageHeader().GetOatFileEnd(); ...... requested_alloc_space_begin = AlignUp(oat_file_end_addr, kPageSize); } ...... } ~~~ 關于Image Space的創建過程,可以參考前面[ART運行時Java堆創建過程分析](http://blog.csdn.net/luoshengyang/article/details/42379729)一文。從前面[ART運行時Java堆創建過程分析](http://blog.csdn.net/luoshengyang/article/details/42379729)一文可以知道,緊跟在Image Space后面的是一個boot.art@classes.oat文件。而緊跟在boot.art@classes.oat文件末尾的Zygote Space,這個地址記錄在本地變量requested_alloc_space_begin中。 第二段代碼是關于Non-Moving Space的,如下所示: ~~~ bool support_homogeneous_space_compaction = background_collector_type_ == gc::kCollectorTypeHomogeneousSpaceCompact || use_homogeneous_space_compaction_for_oom; // We may use the same space the main space for the non moving space if we don't need to compact // from the main space. // This is not the case if we support homogeneous compaction or have a moving background // collector type. bool separate_non_moving_space = is_zygote || support_homogeneous_space_compaction || IsMovingGc(foreground_collector_type_) || IsMovingGc(background_collector_type_); if (foreground_collector_type == kCollectorTypeGSS) { separate_non_moving_space = false; } std::unique_ptr<MemMap> main_mem_map_1; std::unique_ptr<MemMap> main_mem_map_2; byte* request_begin = requested_alloc_space_begin; if (request_begin != nullptr && separate_non_moving_space) { request_begin += non_moving_space_capacity; } ...... std::unique_ptr<MemMap> non_moving_space_mem_map; if (separate_non_moving_space) { // Reserve the non moving mem map before the other two since it needs to be at a specific // address. non_moving_space_mem_map.reset( MemMap::MapAnonymous("non moving space", requested_alloc_space_begin, non_moving_space_capacity, PROT_READ | PROT_WRITE, true, &error_str)); ...... // Try to reserve virtual memory at a lower address if we have a separate non moving space. request_begin = reinterpret_cast<byte*>(300 * MB); } ~~~ 這段代碼的邏輯是判斷是否需要給Non-Moving Space一個獨立的地址空間。Non-Moving Space總是存在的,現在需要判斷的是要給它一個獨立的地址空間,還是要與其它Space共享同一個地址空間,主要是考慮到Generational Semi-Space GC。 從前面[ART運行時Compacting GC簡要介紹和學習計劃](http://blog.csdn.net/luoshengyang/article/details/44513977)一文可以知道,Generational Semi-Space GC需要一個Promote Space來保存那些經過若干輪GC后仍然存活下來的對象,而且這些對象在以后的Generational Semi-Space GC中不需要進行移動。這個Promote Space就是一個DlMallocSpace或者RosAllocSpace。Promote Space起到的作用與Non-Moving Space類似,因為保存在它們里面的對象都是不可以移動的。因此,在Generational Semi-Space GC的情況下,將Promote Space和Non-Moving Space合在一起共享同一個地址空間。 Non-Moving Space是相對Moving Space而言的,也就是說,只要存在Moving Space,就需要給Non-Moving Space一個獨立的地址空間,使得在Non-Moving Space和Moving Space的對象在GC中可以區別對待處理。 那么,在什么情況下存在Moving Space呢?最直覺地,只要我們使用到了Compacting GC,那么就需要Moving Space,因為Compacting GC需要移動對象。因此,上述代碼段會調用Heap類的成員函數IsMovingGc判斷指定的Foreground GC(foreground_collector_type_)和Background GC(background_collector_type_)是否是Compacting GC,也就是是否是Semi-Space GC、Generational Semi-Space GC和Mark-Compact GC之一。如果是的話,那么就將本地變量separate_non_moving_space設置為true,表示需要給Non-Moving Space一個獨立的地址空間。 除了Compacting GC的情況,還有兩種情況也是涉及到Moving Space的。 * 第一種情況是應用程序運行在Zygote模式中,即本地變量is_zygote等于true的情況下。應用程序運行在Zygote模式時,它們的進程都是由Zygote進程fork出來的,這樣做的目的是為了讓Zygote進程和應用程序進程共享內存。Zygote進程在fork第一個應用程序進程之前,為了有效地和應用程序進程共享內存,會對堆空間進行一次壓縮處理。這個壓縮處理實際上就是執行一次Semi-Space GC。因此,在這種情況下,即本地變量is_zygote等于true時,也需要將本地變量separate_non_moving_space設置為true,表示需要給Non-Moving Space一個獨立的地址空間。 * 第二種情況ART運行時支持Homogeneous-Space-Compact特性。Homogeneous-Space-Compact特性意味我們要將Main Space上的對象移動到Backup Space上去。這個移動過程實際上也是通過執行一次Semi-Space GC來完成的。因此,在這種情況下,即本地變量support_homogeneous_space_compaction等于true時,也需要將本地變量separate_non_moving_space設置為true,表示需要給Non-Moving Space一個獨立的地址空間。 那么,什么情況下ART運行時需要支持Homogeneous-Space-Compact特性呢?有兩種情況需要支持。 * 第一種情況是Background GC(background_collector_type_)被指定為Homogeneous-Space-Compact GC,這可以通過ART運行時啟動選項-XX:BackgroundGC進行指定。 * 第二種情況是在分配對象遇到OOM時,需要將Main Space上的對象移動到Backup Space上去,然后再將這兩個Space進行交換,并且再次嘗試在Main Space上進行分配,以便可以解決由內存碎片引發的OOM問題。我們可以通過ART運行時啟動選項-XX:EnableHSpaceCompactForOOM和-XX:DisableHSpaceCompactForOOM來啟用和禁用這種行為,體現在這里就是參數use_homogeneous_space_compaction_for_oom的值是等于true還是false。 一旦決定給Non-Moving Space一個獨立的地址空間,那么就會調用MemMap類的靜態成員函數MapAnonymous創建一塊匿名共享內存non_moving_space_mem_map,以便接下來可以用來創建Non-Moving Space。注意,這塊匿名共享內存的起始地址緊接著在boot.art@classes.oat的末尾。同時,其它的Space的起始地址request_begin被修改為300MB地址處,即它們不再是緊跟著Non-Moving Space的末尾。 第三段代碼用來創建另外兩塊匿名共享內存,如下所示: ~~~ // Attempt to create 2 mem maps at or after the requested begin. main_mem_map_1.reset(MapAnonymousPreferredAddress(kMemMapSpaceName[0], request_begin, capacity_, PROT_READ | PROT_WRITE, &error_str)); ...... if (support_homogeneous_space_compaction || background_collector_type_ == kCollectorTypeSS || foreground_collector_type_ == kCollectorTypeSS) { main_mem_map_2.reset(MapAnonymousPreferredAddress(kMemMapSpaceName[1], main_mem_map_1->End(), capacity_, PROT_READ | PROT_WRITE, &error_str)); ...... } ~~~ 第一塊匿名共享內存main_mem_map_1用來創建Compacting GC的From Bump Pointer Space或者Mark-Sweep GC的Main Space。第二塊匿名共享內存main_mem_map_2用來創建Semi-Space GC的To Bump Pointer Space或者Mark-Sweep GC的Backup Space。注意,第二塊匿名共享內存main_mem_map_2緊跟在第一塊匿名共享內存main_mem_map_1的末尾。 第四段代碼用來創建Non-Moving Space,如下所示: ~~~ // Create the non moving space first so that bitmaps don't take up the address range. if (separate_non_moving_space) { // Non moving space is always dlmalloc since we currently don't have support for multiple // active rosalloc spaces. const size_t size = non_moving_space_mem_map->Size(); non_moving_space_ = space::DlMallocSpace::CreateFromMemMap( non_moving_space_mem_map.release(), "zygote / non moving space", kDefaultStartingSize, initial_size, size, size, false); non_moving_space_->SetFootprintLimit(non_moving_space_->Capacity()); ...... AddSpace(non_moving_space_); } ~~~ 只有在本地變量separate_non_moving_space等于true的情況下,也就是要給Non-Moving Space一塊獨立的地址空間的情況下,這里才會將前面創建的匿名共享內存non_moving_space_mem_map封裝成一個DlMallocSpace,作為一塊獨立的Non-Moving Space使用。 第五段代碼用來為Compacting GC創建Bump Pointer Space或者為Mark-Sweep GC創建Main Space和Backup Space,如下所示: ~~~ // Create other spaces based on whether or not we have a moving GC. if (IsMovingGc(foreground_collector_type_) && foreground_collector_type_ != kCollectorTypeGSS) { // Create bump pointer spaces. // We only to create the bump pointer if the foreground collector is a compacting GC. // TODO: Place bump-pointer spaces somewhere to minimize size of card table. bump_pointer_space_ = space::BumpPointerSpace::CreateFromMemMap("Bump pointer space 1", main_mem_map_1.release()); ...... AddSpace(bump_pointer_space_); temp_space_ = space::BumpPointerSpace::CreateFromMemMap("Bump pointer space 2", main_mem_map_2.release()); ...... AddSpace(temp_space_); ...... } else { CreateMainMallocSpace(main_mem_map_1.release(), initial_size, growth_limit_, capacity_); ...... AddSpace(main_space_); if (!separate_non_moving_space) { non_moving_space_ = main_space_; ...... } if (foreground_collector_type_ == kCollectorTypeGSS) { ...... // Create bump pointer spaces instead of a backup space. main_mem_map_2.release(); bump_pointer_space_ = space::BumpPointerSpace::Create("Bump pointer space 1", kGSSBumpPointerSpaceCapacity, nullptr); ...... AddSpace(bump_pointer_space_); temp_space_ = space::BumpPointerSpace::Create("Bump pointer space 2", kGSSBumpPointerSpaceCapacity, nullptr); ...... AddSpace(temp_space_); } else if (main_mem_map_2.get() != nullptr) { const char* name = kUseRosAlloc ? kRosAllocSpaceName[1] : kDlMallocSpaceName[1]; main_space_backup_.reset(CreateMallocSpaceFromMemMap(main_mem_map_2.release(), initial_size, growth_limit_, capacity_, name, true)); ...... // Add the space so its accounted for in the heap_begin and heap_end. AddSpace(main_space_backup_.get()); } } ~~~ 當Foreground GC是Compacting GC,但是不是Generational Semi-Space GC時,分別是用前面創建的匿名共享內存main_mem_map_1和main_mem_map_2創建兩個Bump Pointer Space,并且保存在Heap類的成員變量bump_pointer_space_和temp_space_中。 當Foreground GC是Mark-Sweep GC或者Generational Semi-Space GC時,首先是調用Heap類的成員函數CreateMainMallocSpace創建一個Main Space,這個Main Space是一塊DlMallocSpace或者RosAllocSpace,并且由Heap類的成員變量main_space_指向。 如果Foreground GC是Generational Semi-Space GC,上面創建的Main Space實際上是作為Promote Space來使用的。同時由前面的分析可以知道,本地變量separate_non_moving_space的值這時候等于false,這意味著Non-Moving Space與上述Promote Space共享的是同一個地址空間。也就是此時ART運行時的Non-Moving Space(non_moving_space_)與Generational Semi-Space GC的Promote Space(main_space_)指向的是一個Space。接下來,上述代碼還會繼續為Generational Semi-Space GC創建一個From Bump Pointer Space和一個To Bump Pointer Space。這兩個Bump Pointer Space是通過封裝兩塊新創建的匿名共享內存得到的。 如果Foreground GC是Mark-Sweep GC,則它們所需要的Main Space前面已經創建完畢,現在只需要再創建一個Backup Space即可。通過調用Heap類的成員函數CreateMallocSpaceFromMemMap即可創建一個DlMallocSpace或者RosAllocSpace,以作為Backup Space使用,并且由Heap類的成員變量main_space_backup_指向。 接下來我們繼續分析Heap類的成員函數CreateMainMallocSpace的實現,以便可以了解Main Space的創建過程,而且從中也可以看到用來創建Backup Space的Heap類的成員函數CreateMallocSpaceFromMemMap的實現,如下所示: ~~~ void Heap::CreateMainMallocSpace(MemMap* mem_map, size_t initial_size, size_t growth_limit, size_t capacity) { // Is background compaction is enabled? bool can_move_objects = IsMovingGc(background_collector_type_) != IsMovingGc(foreground_collector_type_) || use_homogeneous_space_compaction_for_oom_; // If we are the zygote and don't yet have a zygote space, it means that the zygote fork will // happen in the future. If this happens and we have kCompactZygote enabled we wish to compact // from the main space to the zygote space. If background compaction is enabled, always pass in // that we can move objets. if (kCompactZygote && Runtime::Current()->IsZygote() && !can_move_objects) { // After the zygote we want this to be false if we don't have background compaction enabled so // that getting primitive array elements is faster. // We never have homogeneous compaction with GSS and don't need a space with movable objects. can_move_objects = !have_zygote_space_ && foreground_collector_type_ != kCollectorTypeGSS; } if (collector::SemiSpace::kUseRememberedSet && main_space_ != nullptr) { RemoveRememberedSet(main_space_); } const char* name = kUseRosAlloc ? kRosAllocSpaceName[0] : kDlMallocSpaceName[0]; main_space_ = CreateMallocSpaceFromMemMap(mem_map, initial_size, growth_limit, capacity, name, can_move_objects); SetSpaceAsDefault(main_space_); VLOG(heap) << "Created main space " << main_space_; } ~~~ 這個函數定義在文件art/runtime/runtime.cc中。 一般來說,在以下兩種情況下,Main Space的對象可以移動: 1. Foreground GC和Background GC不同時為Compacting GC或者Mark-Sweep GC。這是因為當發生Foreground GC和Background GC切換時,如果Foreground GC和Background GC不同時為Compacting GC或者Mark-Sweep GC時,需要將對象從Main Space移動到Bump Pointer Space,或者從Bump Pointer Space移動到Main Space。 2. ART運行時分配對象發生OOM時支持Homogeneous-Space-Compact特性。這時候需要將Main Space的對象移動到Backup Space。 還有一種特殊情況,要求Main Space上的對象是可以移動的。前面提到,Zygote進程在fork第一個應用程序進程之前,會對堆進行一次Semi-Space GC。取決于當前的Foreground GC是Compacting GC還是Mark-Sweep GC,這次Semi-Space GC的From Space即為Compacting GC當前使用的Bump Pointer Space或者Mark-Sweep GC的Main Space。不過這樣的Semi-Space GC是要在常量kCompactZygote設置為true的情況下才會執行。 根據前面的分析,在Foreground GC是Generational Semi-Space GC的情況下,這里創建的Main Space同時也作為Generational Semi-Space GC的Promote Space,這就要求Main Space是不能移動對象的。 有了這些背景知識后,就可以很容易理解Heap類的成員函數CreateMallocSpaceFromMemMap的實現了。首先,語句IsMovingGc(background_collector_type_) != IsMovingGc(foreground_collector_type_)就是用來判斷Foreground GC和Background GC不同時為Compacting GC或者Mark-Sweep GC的。其次,use_homogeneous_space_compaction_for_oom_代表ART運行時分配對象發生OOM時支持Homogeneous-Space-Compact特性。 如果經過上面的處理之后,本地變量can_move_objects的值仍然為false,并且當前是運行在Zygote模式中(Runtime::Current()->IsZygote()等于true)、常量kCompactZygote為true,那么就會接著判斷當前是否處于Zygote進程fork第一個應用程序進程之前,即Heap類的成員變量have_zygote_space_等于false。如果是的話,那么就會在當前的Foreground GC不是Generational Semi-Space GC的情況下,將本地變量can_move_objects修改為true,以便接下來調用Heap類的成員函數CreateMallocSpaceFromMemMap創建一個DlMallocSpace或者RosAllocSpace,如下所示: ~~~ space::MallocSpace* Heap::CreateMallocSpaceFromMemMap(MemMap* mem_map, size_t initial_size, size_t growth_limit, size_t capacity, const char* name, bool can_move_objects) { space::MallocSpace* malloc_space = nullptr; if (kUseRosAlloc) { // Create rosalloc space. malloc_space = space::RosAllocSpace::CreateFromMemMap(mem_map, name, kDefaultStartingSize, initial_size, growth_limit, capacity, low_memory_mode_, can_move_objects); } else { malloc_space = space::DlMallocSpace::CreateFromMemMap(mem_map, name, kDefaultStartingSize, initial_size, growth_limit, capacity, can_move_objects); } if (collector::SemiSpace::kUseRememberedSet) { accounting::RememberedSet* rem_set = new accounting::RememberedSet(std::string(name) + " remembered set", this, malloc_space); CHECK(rem_set != nullptr) << "Failed to create main space remembered set"; AddRememberedSet(rem_set); } CHECK(malloc_space != nullptr) << "Failed to create " << name; malloc_space->SetFootprintLimit(malloc_space->Capacity()); return malloc_space; } ~~~ 這個函數定義在文件art/runtime/runtime.cc中。 如果常量kUseRosAlloc的值等于true,那么就Heap類的成員函數CreateMallocSpaceFromMemMap創建的是一個RosAllocSpace;否則的話,創建的是一個DlMallocSpace。同時,如果常量collector::SemiSpace::kUseRememberedSet的值等于true,那么就為前面創建的RosAllocSpace或者DlMallocSpace創建一個RememberedSet。RememberedSet與在前面[ART運行時垃圾收集機制簡要介紹和學習計劃](http://blog.csdn.net/luoshengyang/article/details/42072975)這個系列文章提到的ModUnionTable的作用類似,都是用來記錄被修改對象對指定目標空間的對象的引用情況的。 回到Heap類的成員函數CreateMainMallocSpace中,調用Heap類的成員函數CreateMallocSpaceFromMemMap創建完成Main Space之后,還會調用另外一個成員函數SetSpaceAsDefault將該Main Space設置為當前Mark-Sweep GC使用的Main Space或者當前Generational Semi-Space GC使用的Promote Space,如下所示: ~~~ void Heap::SetSpaceAsDefault(space::ContinuousSpace* continuous_space) { WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); if (continuous_space->IsDlMallocSpace()) { dlmalloc_space_ = continuous_space->AsDlMallocSpace(); } else if (continuous_space->IsRosAllocSpace()) { rosalloc_space_ = continuous_space->AsRosAllocSpace(); } } ~~~ 這個函數定義在文件art/runtime/runtime.cc中。 如果前面創建的Main Space是一個DlMallocSpace,那么就將它保存在Heap類的成員變量dlmalloc_space_中;否則的話,如果是一個RosAllocSpace,就保存在Heap類的成員變量rosalloc_space_中。 設置好Heap類的成員變量dlmalloc_space_和rosalloc_space_之后,以后在分配對象時,就可以通過kAllocatorTypeDlMalloc或者kAllocatorTypeRosAlloc常量指定是要DlMallocSpace中分配對象,還是在RosAllocSpace中分配對象,以及Generational Semi-Space GC可以通過它們獲得對應的Promote Space來保存那些經過若干輪GC仍然存活下來的對象。 代碼分析到這里,我們就基本上把圖1、圖2和圖3涉及到的知識都解釋完畢,大家可以對照著重新理解一下。不過,在圖1、圖2和圖3中,我們還沒有解釋到的一個點就是Zygote Space是怎么來的。接下來我們就繼續分析Zygote Space的創建過程。 Zygote Space是從Non-Moving Space分割而來的。具體來說,就是在Zygote進程fork第一個應用程序進程之前,會調用Heap類的成員函數PreZygoteFork創建一個Zygote Space,它的實現如下所示: ~~~ void Heap::PreZygoteFork() { CollectGarbageInternal(collector::kGcTypeFull, kGcCauseBackground, false); Thread* self = Thread::Current(); MutexLock mu(self, zygote_creation_lock_); // Try to see if we have any Zygote spaces. if (have_zygote_space_) { return; } ...... // Trim the pages at the end of the non moving space. non_moving_space_->Trim(); // The end of the non-moving space may be protected, unprotect it so that we can copy the zygote // there. non_moving_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE); const bool same_space = non_moving_space_ == main_space_; if (kCompactZygote) { ...... // Temporarily disable rosalloc verification because the zygote // compaction will mess up the rosalloc internal metadata. ScopedDisableRosAllocVerification disable_rosalloc_verif(this); ZygoteCompactingCollector zygote_collector(this); zygote_collector.BuildBins(non_moving_space_); // Create a new bump pointer space which we will compact into. space::BumpPointerSpace target_space("zygote bump space", non_moving_space_->End(), non_moving_space_->Limit()); // Compact the bump pointer space to a new zygote bump pointer space. bool reset_main_space = false; if (IsMovingGc(collector_type_)) { zygote_collector.SetFromSpace(bump_pointer_space_); } else { ...... // Copy from the main space. zygote_collector.SetFromSpace(main_space_); reset_main_space = true; } zygote_collector.SetToSpace(&target_space); zygote_collector.SetSwapSemiSpaces(false); zygote_collector.Run(kGcCauseCollectorTransition, false); if (reset_main_space) { main_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE); madvise(main_space_->Begin(), main_space_->Capacity(), MADV_DONTNEED); MemMap* mem_map = main_space_->ReleaseMemMap(); RemoveSpace(main_space_); space::Space* old_main_space = main_space_; CreateMainMallocSpace(mem_map, kDefaultInitialSize, mem_map->Size(), mem_map->Size()); delete old_main_space; AddSpace(main_space_); } ...... } ...... space::MallocSpace* old_alloc_space = non_moving_space_; ...... space::ZygoteSpace* zygote_space = old_alloc_space->CreateZygoteSpace("alloc space", low_memory_mode_, &non_moving_space_); ...... if (same_space) { main_space_ = non_moving_space_; SetSpaceAsDefault(main_space_); } delete old_alloc_space; ...... AddSpace(zygote_space); non_moving_space_->SetFootprintLimit(non_moving_space_->Capacity()); AddSpace(non_moving_space_); have_zygote_space_ = true; ...... accounting::ModUnionTable* mod_union_table = new accounting::ModUnionTableCardCache("zygote space mod-union table", this, zygote_space); ...... AddModUnionTable(mod_union_table); if (collector::SemiSpace::kUseRememberedSet) { // Add a new remembered set for the post-zygote non-moving space. accounting::RememberedSet* post_zygote_non_moving_space_rem_set = new accounting::RememberedSet("Post-zygote non-moving space remembered set", this, non_moving_space_); ...... AddRememberedSet(post_zygote_non_moving_space_rem_set); } } ~~~ 這個函數定義在文件art/runtime/runtime.cc中。 Heap類的成員函數PreZygoteFork用來從Non-Moving Space中分割出一個Zygote Space來。在分割之前,如果需要的話,還會對Main Space或者Bump Pointer Space的對象進行壓縮處理。我們分成三小段代碼來閱讀這個函數。 第一段代碼是對堆進行一次GC和裁剪處理,如下所示: ~~~ CollectGarbageInternal(collector::kGcTypeFull, kGcCauseBackground, false); Thread* self = Thread::Current(); MutexLock mu(self, zygote_creation_lock_); // Try to see if we have any Zygote spaces. if (have_zygote_space_) { return; } ...... // Trim the pages at the end of the non moving space. non_moving_space_->Trim(); // The end of the non-moving space may be protected, unprotect it so that we can copy the zygote // there. non_moving_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE); const bool same_space = non_moving_space_ == main_space_; ~~~ 對堆進行GC處理是通過調用Heap類的成員函數CollectGarbageInternal來實現的。接下來,判斷Heap類的成員變量have_zygote_space_的值是否等于true。如果等于的話,就說明Zygote Space已經創建過了,因此就不用再往下處理。否則的話,再繼續調用Heap類的成員變量non_moving_space_指向的一個DlMallocSpace或者RosAllocSpace對象的成員函數Trim對它未使用的內存進行裁剪,即將這些未使用的內存歸還給內核。最后,將Non-Moving Space內部使用的匿名共享內存塊設置為可讀可寫,并且記錄好Non-Moving Space和Main Space是否共用同一塊匿名共享內存。這里之所以要將Non-Moving Space內部使用的匿名共享內存塊設置為可讀可寫,是因為接下來我們要將Main Space或者Bump Pointer Space上的對象移動到Non-Moving Space上去。 第二段代碼是將Main Space或者Bump Pointer Space上的對象移動到Non-Moving Space上去,如下所示: ~~~ if (kCompactZygote) { ...... // Temporarily disable rosalloc verification because the zygote // compaction will mess up the rosalloc internal metadata. ScopedDisableRosAllocVerification disable_rosalloc_verif(this); ZygoteCompactingCollector zygote_collector(this); zygote_collector.BuildBins(non_moving_space_); // Create a new bump pointer space which we will compact into. space::BumpPointerSpace target_space("zygote bump space", non_moving_space_->End(), non_moving_space_->Limit()); // Compact the bump pointer space to a new zygote bump pointer space. bool reset_main_space = false; if (IsMovingGc(collector_type_)) { zygote_collector.SetFromSpace(bump_pointer_space_); } else { ...... // Copy from the main space. zygote_collector.SetFromSpace(main_space_); reset_main_space = true; } zygote_collector.SetToSpace(&target_space); zygote_collector.SetSwapSemiSpaces(false); zygote_collector.Run(kGcCauseCollectorTransition, false); if (reset_main_space) { main_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE); madvise(main_space_->Begin(), main_space_->Capacity(), MADV_DONTNEED); MemMap* mem_map = main_space_->ReleaseMemMap(); RemoveSpace(main_space_); space::Space* old_main_space = main_space_; CreateMainMallocSpace(mem_map, kDefaultInitialSize, mem_map->Size(), mem_map->Size()); delete old_main_space; AddSpace(main_space_); } ...... } ~~~ 當常量kCompactZygote的值等于true的情況下,就需要將當前Main Space或者Bump Pointer Space上的對象移動到Non-Moving Space上去。 這個移動的過程是通過一個類型為ZygoteCompactingCollector的垃圾收集器來完成的。ZygoteCompactingCollector是從SemiSpace繼承下來的,這意味著它是通過執行一次Semi-Space GC來完成對象的移動過程的。 實際上,ZygoteCompactingCollector執行的是一次特殊的Semi-Space GC。通常我們執行Semi-Space GC時,涉及到From和To兩個Space。其中,From Space包含有對象,而To Space完全沒有包含對象。這樣當我們將對象從From Space移動到To Space時,就從To Space的起始位置開始保存對象。但是對于ZygoteCompactingCollector來說,它需要將Main Space或者Bump Pointer Space的對象移動到Non-Moving Space上去,但是Non-Moving Space這時候可能不是空的,也就是說,在上面已經存在一些對象,而且這些對象在地址空間上可能不是連續地存在的。 在移動對象之前,ZygoteCompactingCollector將Non-Moving Space分為兩部分。第一部分是前面包含有對象的空間,這部分空間可能存在一些空閑內存,因此就調用ZygoteCompactingCollector類的成員函數BuildBins將這些空閑內存塊的起始地址和大小記錄起來。第二部分是后面完全沒有包含對象的空間,這部分空間被封裝為一個BumpPointerSpace,作為ZygoteCompactingCollector的To Space。于是在移動對象到Non-Moving Space上的時候,就會優先考慮前面的空閑內存塊是否合適用來保存一個被移動對象。如果合適的話,就使用它;否則的話,再將被移動對象保存在To Space中。通過這種方式,就可以最有效地利用Non-Moving Space,盡最大限度減小內存碎片。 設置好ZygoteCompactingCollector的To Space之后,接下來再設置它的From Space。如果當前的Foreground GC是一個Compacting GC,那么就意味著當前使用的Space是一個Bump Pointer Space,該Bump Pointer Space由Heap類的成員變量bump_pointer_space_指向。否則的話,當前的Foreground GC就是一個Mark-Sweep GC,這意味著當前使用的Space是一個Main Space,該Main Space由Heap類的成員變量main_space_指向。在后一種情況下,還需要將本地變量reset_main_space的設置為true,表示在移動對象完成之后,需要重置Main Space。 設置好ZygoteCompactingCollector的From Space和To Space之后,就可以調用它的成員函數Run進行Semi-Space GC了。Semi-Space GC執行完畢,如果前面將本地變量reset_main_space的設置為true,就說明我們是將Main Space上的對象移動到了Non-Moving Space中。這時候Main Space就沒有什么作用了,這時候就可以將Main Space之前占用的所有內存都可以歸還給內核。但是,我們還是需要有一個Main Space的,因此,就再重新調用我們前面分析過的Heap類的成員函數CreateMainMallocSpace創建一個Main Space,并且將新創建的Main Space最初始占用的內存大小設置為kDefaultInitialSize。通過這個重置操作,就可以減少Main Space占用的內存。 第三段代碼執行從Non-Moving Space分離出Zygote Space的操作,如下所示: ~~~ space::MallocSpace* old_alloc_space = non_moving_space_; ...... space::ZygoteSpace* zygote_space = old_alloc_space->CreateZygoteSpace("alloc space", low_memory_mode_, &non_moving_space_); ...... if (same_space) { main_space_ = non_moving_space_; SetSpaceAsDefault(main_space_); } delete old_alloc_space; ...... AddSpace(zygote_space); non_moving_space_->SetFootprintLimit(non_moving_space_->Capacity()); AddSpace(non_moving_space_); have_zygote_space_ = true; ...... accounting::ModUnionTable* mod_union_table = new accounting::ModUnionTableCardCache("zygote space mod-union table", this, zygote_space); ...... AddModUnionTable(mod_union_table); if (collector::SemiSpace::kUseRememberedSet) { // Add a new remembered set for the post-zygote non-moving space. accounting::RememberedSet* post_zygote_non_moving_space_rem_set = new accounting::RememberedSet("Post-zygote non-moving space remembered set", this, non_moving_space_); ...... AddRememberedSet(post_zygote_non_moving_space_rem_set); } ~~~ 本地變量old_alloc_space指向舊的Non-Moving Space,通過調用它的成員函數CreateZygoteSpace可以從里面分割出一個Zygote Space出來,保存在本地變量zygote_space中,并且新的Non-Moving Space仍然保存在Heap類的成員變量non_moving_space_中。從Non-Moving Space分割出一個Zygote Space可以參考前面[ART運行時Java堆創建過程分析](http://blog.csdn.net/luoshengyang/article/details/42379729)一文分析的從Allocation Space分割出Zygote Space的方法。 從舊的Non-Moving Space分割出Zygote Space和新的Non-Moving Space之后,如果前面記錄了Main Space和Non-Moving Space共享的是同一塊地址空間,那么同時也需要修改Heap類的成員變量main_space_的值,使得它與新的Non-Moving Space指向的是同一塊地址空間,并且調用Heap類的成員函數SetSpaceAsDefault將新的Main Space設置為當前使用的DlMallocSpace或者RosAllocSpace。 接下來還需要將Heap類的成員變量have_zygote_space_設置為true,表示Zygote Space已經從Non-Moving Space分割出來了。最后,還要為新創建的Zygote Space創建一個ModUnionTable,用來記錄該Space的對象被修改時對其它Space的引用情況。同時,在常量collector::SemiSpace::kUseRememberedSet為true的情況下,為新的Non-Moving Space創建一個RememberedSet,同樣是用來記錄該Space的對象被修改時對其它Space的引用情況。 這樣,從Non-Moving Space中分割出Zygote Space的總體過程就分析完成了。由于具體過程涉及到Semi-Space GC,這里就沒有進一步展開來說,不過接下來我們會有專門的文章分析Semi-Space GC的執行過程,到時候再回過頭分析從Non-Moving Space中分割出Zygote Space的具體過程就會容易很多了。 至此,圖1、圖2和圖3涉及到的所有知識點就分析完成了,從中我們就可以了解到ART運行時引進了Compacting GC之后,內部的堆空間結構組成。這對我們后面理解ART運行時的對象分配過程以及Compacting GC的執行過程都是非常重要的。在接下來的一篇文章中,我們就對引進了Compacting GC之后的ART運行時的對象分配過程進行分析,
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看