最新诸葛马:V8系列——内存管理(1)

来源:百度文库 编辑:偶看新闻 时间:2024/04/30 03:19:31

高效的程序离不开内存的有效管理。自己对内存管理的好处不少:减少内存分配、回收开销、避免内存碎片、定位内存位置、方便内存整理、跟踪内存使用等等。V8 的堆内存Heap用于存预编译的codeJS对象内存分配、运行上下文对象分配、垃圾回收等。

一、内存的建构(Heap::Setup)

1、在V8完成OS操作的setup后,随即建立和管理内存;首先配置Heap参数,Heap分为Young Generaion & Old GenerationYoung Generation被划分为两个semispace,每个semispace大小默认为2MBOld Gernation 默认大小为512MB;概念上讲Young Generation = new spaceOld Generation = old space,即Heap又可以看成被划分成若干个space(既有free space,也有PagedSpace,即空间的大小与内存页大小对齐),每个space专门负责相应对象的内存分配和回收。

2Heap 接着配置内存分配器MemoryAllocatorsingleton),让之为heap生成ChunkInfo信息,其中包括了内存地址、大小以及所属的PagedSpaceHeap被划分成若干个chunk,每个chunk大小=PagesPerChunk*PageSize = 64*8K(视操作系统内存页而定)= 512K。同时内存分配器预留分配了一块2*Young Generation大小(8MB)的虚拟内存(initial_chunk_)

后续所有内存的分配都得通过MemoryAllocator进行,setup时候设置了最大的sizeYoung Generation Size + Old Generation Size,即默认为516MB

3、接着Heap对该块虚拟内存进行如下空间划分:


其中CodeSpaceOldSpace共享4MB的空间划分策略是否可以优化?

4、每个spacesetup

4.1)各个space的关系如下:


4.2)各个space的作用:

A. LargeObjectSpace :为了避免大对象的拷贝,使用该空间专门存储大对象(大小超过Normal Page能容纳的对象范围),包括CodeSequetial StringFixedArray

B. MapSpace :存放对象的Map信息,即hidden_class;最大限制为8MB;每个Map对象固定大小,为了快速定位,所以将该空间单独出来;

C. NewSpace :存放多种类型对象,最大限制为2MB

D. CodeSpace :存放预编译代码(?);最大限制为512MB

E. Old_Pointer_Space :存放GCsurviving的指针对象;最大限制为512MB

F. Old_Data_Space :存放GC后surviving的数据对象;最大限制为512MB;

二、内存的析构(Heap::TearDown)

new_space_old_pointer_space_old_data_space_code_space_map_space_lo_space_依次析构,最后是内存分配器MemoryAllocator::TearDown

三、空间的内存分配

根据对象类型、对象生命状态,在对应的空间中分配内存,总的内存申请入口在Heap::AllocateRaw。每个space的相同之处,都使用AllocationInfo记录空间的toplimit,即当前可用内存起始地址和终止地址。但不同space的内存分配逻辑不同,具体表现为:

1、 PagedSpace的内存分配

由于PagedSpace空间由多个page组成,page的结构如下:

为了避免申请的内存跨页(即避免内存缺页中断,从而增加对象访问延迟),所以分配内存步骤可以划分为:

1)如果申请内存大小没有超过current_page限制,则直接划分出该大小区域,并往前移动top指针(增加地址);

2)通过current_pageopaque_header查找到nextpage,并询问其是否可用,是则把AllocationInfo更新为nextpage,返回(1);此外,对于current_page剩余的内存块,不同的派生类处理不同,MapSpace直接将之丢弃,可能是考虑到map大小固定,每个page浪费掉PageSize%MapSize,空间不算大,不过它仍然有free list,收集map对象回收后的内存;OldSpace则将之放入到一个free list,供后面使用;

3)从空间的free list查找是否有可用的内存块,MapSpace free list直接把头结点划分出去,而OldSpaceFreeList::Allocate复杂些,后续讨论;

4)扩展空间,MemoryAllocator::AllocatePages转而请求分配虚拟内存,在不超过最大空间限制前提下扩展一个chunk,即64page,返回(2);

5)返回Failure::RetryAfterGC,回收内存垃圾并重试。后续会解释如果利用该返回结果进行GC并重试。

具体流程如下:


      从PagedSpace::Expand可以窥见该space的初始化逻辑如下:

a)指定需要分配的Page数目,交给内存配置器MemoryAllocator执行

b)MemoryAllocator自身也有最大内存数目的限制,不能保证刚才指定的page数目,只有最大限度地满足,不然只有返回错误;

c)初始化chunk中的所有Page信息;



2、NewSpace的内存分配:如果当前空间满足申请大小,则直接分配,否则返回Failure::RetryAfterGC,回收内存垃圾并重试。

3LargeObjectSpace的内存分配

    由于该空间中每个Page都只会存放一个对象,所以当申请内存块时,直接通过MemoryAllocator::AllocateRawMemory分出一块对象大小的内存,并加入到该空间的内存块管理链表中。