NewSpace是v8内存管理中,负责管理新生代区的类。分为from和to两个区,每个区由SemiSpace对象管理。和SemiSpace一样,NewSpace也不负责内存的分配和释放,他只负责内存的使用和管理。下面是类的定义。
代码语言:javascript复制class NewSpace : public Malloced {
public:
NewSpace(int initial_semispace_capacity, int maximum_semispace_capacity);
bool Setup(Address start, int size);
void TearDown();
bool HasBeenSetup() {
return to_space_->HasBeenSetup() && from_space_->HasBeenSetup();
}
void Flip();
bool Double();
// 判断地址a是否属于NewSpace管理的内存范围,address_mask_是用于清除a的低n位。见SetUp函数
bool Contains(Address a) {
return (reinterpret_cast<uint32_t>(a) & address_mask_)
== reinterpret_cast<uint32_t>(start_);
}
// 判断堆对象是不是在NewSpace管理的内存里,object_mask_和object_expected_见SetUp函数
bool Contains(Object* o) {
return (reinterpret_cast<uint32_t>(o) & object_mask_) == object_expected_;
}
// to区已分配的内存大小
int Size() { return top() - bottom(); }
int Capacity() { return capacity_; }
// to区还有多少内存可用
int Available() { return Capacity() - Size(); }
int MaximumCapacity() { return maximum_capacity_; }
// 当前已经分配出去的内存的末地址
Address top() { return allocation_info_.top; }
// to_space的管理的内存的首地址
Address bottom() { return to_space_->low(); }
Address age_mark() { return from_space_->age_mark(); }
void set_age_mark(Address mark) { to_space_->set_age_mark(mark); }
Address start() { return start_; }
uint32_t mask() { return address_mask_; }
// 当前已分配的内存的末地址
Address* allocation_top_address() { return &allocation_info_.top; }
// 最大能分配的内存末地址
Address* allocation_limit_address() { return &allocation_info_.limit; }
Object* AllocateRaw(int size_in_bytes) {
return AllocateRawInternal(size_in_bytes, &allocation_info_);
}
Object* MCAllocateRaw(int size_in_bytes) {
return AllocateRawInternal(size_in_bytes, &mc_forwarding_info_);
}
void ResetAllocationInfo();
void MCResetRelocationInfo();
void MCCommitRelocationInfo();
Address FromSpaceLow() { return from_space_->low(); }
Address FromSpaceHigh() { return from_space_->high(); }
// to区管理的内存的首地址和末地址
Address ToSpaceLow() { return to_space_->low(); }
Address ToSpaceHigh() { return to_space_->high(); }
// 地址a距离to区管理的内存的首地址的偏移
int ToSpaceOffsetForAddress(Address a) {
return to_space_->SpaceOffsetForAddress(a);
}
int FromSpaceOffsetForAddress(Address a) {
return from_space_->SpaceOffsetForAddress(a);
}
// 见SemiSpace
bool ToSpaceContains(Object* o) { return to_space_->Contains(o); }
bool FromSpaceContains(Object* o) { return from_space_->Contains(o); }
bool ToSpaceContains(Address a) { return to_space_->Contains(a); }
bool FromSpaceContains(Address a) { return from_space_->Contains(a); }
private:
int capacity_;
int maximum_capacity_;
SemiSpace* to_space_;
SemiSpace* from_space_;
Address start_;
uint32_t address_mask_;
uint32_t object_mask_;
uint32_t object_expected_;
/*
struct AllocationInfo {
Address top; // current allocation top
Address limit; // current allocation limit
};
*/
AllocationInfo allocation_info_;
AllocationInfo mc_forwarding_info_;
inline Object* AllocateRawInternal(int size_in_bytes,
AllocationInfo* alloc_info);
friend class SemiSpaceIterator;
public:
TRACK_MEMORY("NewSpace")
};
下面看看类的实现。
NewSpace
一个NewSpace分为两个SemiSpace,真正的初始化函数是SetUp
代码语言:javascript复制// 分为两个space
NewSpace::NewSpace(int initial_semispace_capacity,int maximum_semispace_capacity) {
ASSERT(initial_semispace_capacity <= maximum_semispace_capacity);
ASSERT(IsPowerOf2(maximum_semispace_capacity));
maximum_capacity_ = maximum_semispace_capacity;
capacity_ = initial_semispace_capacity;
to_space_ = new SemiSpace(capacity_, maximum_capacity_);
from_space_ = new SemiSpace(capacity_, maximum_capacity_);
}
Setup
初始化NewSpace对象的属性。和from区to区。
代码语言:javascript复制// 设置需要管理的地址空间,start是首地址,size是大小
bool NewSpace::Setup(Address start, int size) {
ASSERT(size == 2 * maximum_capacity_);
ASSERT(IsAddressAligned(start, size, 0));
// to区
if (to_space_ == NULL || !to_space_->Setup(start, maximum_capacity_)) {
return false;
}
// from区,和to区一人一半
if (from_space_ == NULL || !from_space_->Setup(start maximum_capacity_, maximum_capacity_)) {
return false;
}
// 开始地址
start_ = start;
/*
address_mask的高位是地址的有效位,
size是只有一位为一,减一后一变成0,一右边
的全部0位变成1,然后取反,高位的0变成1,再加上size中本来的1,
即从左往右的1位地址有效位
*/
address_mask_ = ~(size - 1);
// 用于判断堆对象是不是在NewSpace管理的内存范围
object_mask_ = address_mask_ | kHeapObjectTag;
object_expected_ = reinterpret_cast<uint32_t>(start) | kHeapObjectTag;
// 初始化管理的地址的信息,to区管理的开始地址和结束地址
allocation_info_.top = to_space_->low();
allocation_info_.limit = to_space_->high();
mc_forwarding_info_.top = NULL;
mc_forwarding_info_.limit = NULL;
ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);
return true;
}
TearDown
重置属性,不负责内存的释放
代码语言:javascript复制// 重置属性,不负责内存的释放
void NewSpace::TearDown() {
start_ = NULL;
capacity_ = 0;
allocation_info_.top = NULL;
allocation_info_.limit = NULL;
mc_forwarding_info_.top = NULL;
mc_forwarding_info_.limit = NULL;
if (to_space_ != NULL) {
to_space_->TearDown();
// 释放SemiSpace对象
delete to_space_;
to_space_ = NULL;
}
if (from_space_ != NULL) {
from_space_->TearDown();
delete from_space_;
from_space_ = NULL;
}
}
Flip
翻转from区和to区。
代码语言:javascript复制// 翻转,在gc中调用
void NewSpace::Flip() {
SemiSpace* tmp = from_space_;
from_space_ = to_space_;
to_space_ = tmp;
}
Double
代码语言:javascript复制// 扩容
bool NewSpace::Double() {
ASSERT(capacity_ <= maximum_capacity_ / 2);
if (!to_space_->Double() || !from_space_->Double()) return false;
capacity_ *= 2;
// 从新扩容的地址开始分配内存,即之前分配的内存的末端。
allocation_info_.limit = to_space_->high();
return true;
}
还有一些重置属性的函数。在垃圾回收的时候再具体分析他的作用。
代码语言:javascript复制// 重置管理内存分配的指针
void NewSpace::ResetAllocationInfo() {
allocation_info_.top = to_space_->low();
allocation_info_.limit = to_space_->high();
}
void NewSpace::MCResetRelocationInfo() {
mc_forwarding_info_.top = from_space_->low();
mc_forwarding_info_.limit = from_space_->high();
}
void NewSpace::MCCommitRelocationInfo() {
allocation_info_.top = mc_forwarding_info_.top;
allocation_info_.limit = to_space_->high();
}
AllocateRawInternal
代码语言:javascript复制// 分配内存
Object* NewSpace::AllocateRawInternal(int size_in_bytes, AllocationInfo* alloc_info) {
// 当前可分配内存的开始地址
Address new_top = alloc_info->top size_in_bytes;
// 内存不够了
if (new_top > alloc_info->limit) {
return Failure::RetryAfterGC(size_in_bytes, NEW_SPACE);
}
// 地址 低一位的标记,转成堆对象的地址表示,即低位置1
Object* obj = HeapObject::FromAddress(alloc_info->top);
// 更新指针,指向下一块可分配的内存
alloc_info->top = new_top;
return obj;
}