一.HeapObject之内存结构
v8使用HeapObject作为js object的基类,其优点是一方面可以加快访问速度,另外还可以通过垃圾回收进行管理,所有从HeapObject派生的类,都是原始的struct结构,它使用四字节作为一个字段,第一个四字节字段是map指针,指向一个map对象,当然这个map对象也是从HeapObject派生的,我们可以从这样的派生类定义中看到其内存结构。以StringObject为例,可参看
StringObject的内存布局,
JSReceiver及其派生类的内存布局
二.HeapObject中字段的设置与获取
HeapObject及其派生类对象中的字段是靠getter和setter来获取和设置的,但是在他们的定义中却找不到getter和setter函数,那么它们定义在哪里呢?
在objects.h中,宏DECL_ACCESSORS定义如下:
#define DECL_ACCESSORS(name, type) \
inline type* name(); \
inline void set_##name(type* value, \
WriteBarrierMode mode = UPDATE_WRITE_BARRIER); \
每个HeapObject中必然有该宏来声明getter和setter函数
在objects-inl.h,宏ACCESSORS定义了getter和setter函数
#define ACCESSORS(holder, name, type, offset) \
type* holder::name() { return type::cast(READ_FIELD(this, offset)); } \
void holder::set_##name(type* value, WriteBarrierMode mode) { \
WRITE_FIELD(this, offset, value); \
CONDITIONAL_WRITE_BARRIER(GetHeap(), this, offset, value, mode); \
}
另外,在该头文件中,还有大量类使用ACCESSORS宏定义其getter和setter函数
该宏中使用了READ_FIELD和WRITE_FIELD宏来读写指定offset的内容
#define FIELD_ADDR(p, offset) \
(reinterpret_cast<byte*>(p) + offset - kHeapObjectTag)
#define READ_FIELD(p, offset) \
(*reinterpret_cast<Object**>(FIELD_ADDR(p, offset)))
#define WRITE_FIELD(p, offset, value) \
(*reinterpret_cast<Object**>(FIELD_ADDR(p, offset)) = value)
三.TypeChecker and Cast
由于使用了Struct的内存结构,无法像C++那样在运行期保证Object的类型,v8定义了如下的宏来保证类型和转换的正确性(在objects-inl.h中定义)
#define TYPE_CHECKER(type, instancetype) \
bool Object::Is##type() { \
return Object::IsHeapObject() && \
HeapObject::cast(this)->map()->instance_type() == instancetype; \
}
判断type是否是instancetype
#define CAST_ACCESSOR(type) \
type* type::cast(Object* object) { \
ASSERT(object->Is##type()); \
return reinterpret_cast<type*>(object); \
}
把Object指针强制转换为type*类型
我们可以看到这里使用了map的instance_type进行实例的类型判断,它们实际上定义了两类函数,TYPE_CHECKER宏为Object类定义IsType函数,CAST_ACCESSOR宏为不同的类定义cast函数,同样在该文件中,若干个类使用了这两个宏来定义IsType和cast函数
四.注意事项
这里特别需要提到的一点是在计算地址的时候,使用到了kHeapObjectTag,该值为1,而且在调试过程中我们发现这里用到计算地址的this指针都不是四字节对齐的,显然在要求四字节对齐的C/C++语言中是不可想象的,所以这个this指针一定会是做了处理,通过分析我们发现在创建Struct对象的时候,Heap分配内存后,会调用HeapObject::FromAddress函数,给四字节对齐加上一个kHeapObjectTag,所以在这里计算地址的时候,要减去这个kHeapObjectTag,否则会发生访问违例。
HeapObject* HeapObject::FromAddress(Address address) {
ASSERT_TAG_ALIGNED(address);
return reinterpret_cast<HeapObject*>(address + kHeapObjectTag);
}
Address HeapObject::address() {
return reinterpret_cast<Address>(this) - kHeapObjectTag;
}
还有另外一点,set和get函数对成员变量操作的时候,传入的类型都是指针,需要特别注意,因为每个成员变量都是4字节的,所以他们被get出来,或者被set的时候,都是以指针的形式,当然这并不意味着它的值就一定是一个真实的地址,也可能是一个数值,被强转为指针
v8的HeapObject解析,布布扣,bubuko.com
v8的HeapObject解析
原文:http://blog.csdn.net/sunbxonline/article/details/20311131