使用該內存佈局來查看內存數據,編譯器watch中看不到這樣的對象
特別需要說明的是length字段,它是一個smi對象,注意不是對象指針,而是對象,該類在objects.h中定義,對於32bit系統,它的最低位必須是0,也就是說它的取值需要右移一位。
例如,在編譯js過程中,使用到了source->length(),其中source是Handle<String>,該函數調用如下:
SMI_ACCESSORS(String, length, kLengthOffset) //objects-inl.h
而SMI_ACCESSORS宏定義如下:
#define SMI_ACCESSORS(holder, name, offset) \
int holder::name() { \
Object* value = READ_FIELD(this, offset); \
return Smi::cast(value)->value(); \
} \
void holder::set_##name(int value) { \
WRITE_FIELD(this, offset, Smi::FromInt(value)); \
}
其中Object* value = READ_FIELD(this, offset);讀取的正是上述內存佈局中的Length處的4字節內容,
接着調用了Smi::cast(value)->value(),其中Smi::cast(value)的是通過如下的宏定義的:
#define CAST_ACCESSOR(type) \
type* type::cast(Object* object) { \
ASSERT(object->Is##type()); \
return reinterpret_cast<type*>(object); \
}
其中object->IsSmi定義如下:
bool Object::IsSmi() {//objects-inl.h
return HAS_SMI_TAG(this);
}
這裏會檢查最低位是否爲0,以確定是否是Smi
#define HAS_SMI_TAG(value) \
((reinterpret_cast<intptr_t>(value) & kSmiTagMask) == kSmiTag)
// Tag information for Smi.
const int kSmiTag = 0;
const int kSmiTagSize = 1;
const intptr_t kSmiTagMask = (1 << kSmiTagSize) - 1;
最後調用結束後,得到的Smi對象,會調用它的value函數,就是右移一位取值
int Smi::value() { //objects-inl.h
return Internals::SmiValue(this);
}
清晰瞭解StringObject的內存佈局之後,我們就可以在調試的時候,通過Handle<String>查看String的值,方法如下:
取出Handle<String>中的值Location指向的值,即String對象指針。由於該對象指針是一個HeapObject*, 所以需要將其減一得到真正的String對象指針。查看該指針所表示的內存地址,第一個四字節是Map指針,第二個四字節是Hash值,第三個四字節是就 是smi對象,將其右移一位,即除以2就得到String對象的字符串長度length,這之後的length個字節就是字符串的內容