Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Memory layout of JavaScript objects in V8

Tags:

c

node.js

v8

I'm looking to writing some C bindings to V8, and so I'll need to figure out the memory layout of the various primitive JavaScript types. Is there any documentation on these details anywhere?

like image 717
Steve Klabnik Avatar asked Jul 23 '15 23:07

Steve Klabnik


People also ask

How is data stored in V8 JS engine memory?

Memory spaces in V8 “Variables in JavaScript (and most other programming languages) are stored in two places: stack and heap. A stack is usually a continuous region of memory allocating local context for each executing function. Heap is a much larger region storing everything allocated dynamically.

How are JavaScript objects stored in memory?

The heap is a different space for storing data where JavaScript stores objects and functions. Unlike the stack, the engine doesn't allocate a fixed amount of memory for these objects. Instead, more space will be allocated as needed. Allocating memory this way is also called dynamic memory allocation.

How much memory does a JavaScript object use?

JavaScript uses double-precision (64-bit) floating point numbers. 64 bits are 8 bytes, but each number actually takes up an average of 9.7 bytes. Likewise, Chrome shows the size of each individual empty array as 32 bytes and the size of each empty object as 56 bytes.

What is V8 heap memory?

In V8, the garbage collector is named Orinoco. It divides the heap memory space into 2 regions: young generation and old generation. This design is based on a generational hypothesis: In most cases, young objects are much more likely to die than old objects. And the young/old generation take different strategies.


2 Answers

You don't need to know data types layout to write C bindings for V8. Object's are never really accessed directly when you work with V8 but through an API - only V8 implementation knows how they are laid out. For example getting a property foo from an object o looks like this in C++:

v8::Handle<v8::Object> o;
v8::Handle<v8::Object> v =
  o->Get(v8::String::NewFromUtf8(isolate, "foo"));

Now to wrap this into C you only need to know how to represent and pass around v8::Handle<T>, then you can write wrappers like:

template<typename T> Handle<T> FromC(v8_handle_t h) { /* ... */ }
template<typename T> v8_handle_t ToC(Handle<T> h) { /* ... */ }

extern "C" v8_handle_t v8_object_get(v8_handle_t self, 
                                     v8_handle_t key) {
  return ToC(FromC<Object>(self)->Get(FromC<Value>(key)));
}

So what's inside v8::Handle<T>? In reality it's just a pointer to some slot which in turn contains an actual pointer to an underlying V8 object. This double indirection exists to enable V8 GC to precisely track which objects are in use in C++ code and to allow it moving this objects.

So theoretically you can define v8_handle_t as an opaque pointer and marshall it like this:

typedef struct v8_object_pointer_t* v8_handle_t;  // Opaque pointer
static_assert(sizeof(v8_handle_t) == sizeof(Handle<Object>))
template<typename T> Handle<T> FromC(v8_handle_t h) {
  return *(Handle<T>*)&h;
}
template<typename T> v8_handle_t ToC(const Handle<T>& h) {
  return *(v8_handle_t*)&h;
}

A minor complication comes from managing the structure called HandleScope that manages Handles. In C++ API it relies on RAII-pattern to manage some backing storage. The simplest way to deal with it probably is:

typedef struct {
  void* a[3];
} v8_handle_scope_t;
static_assert(sizeof(v8_handle_scope_t) == sizeof(HandleScope))
void v8_handle_scope_enter(v8_handle_scope_t* scope) {
  new(scope) HandleScope; 
}
void v8_handle_scope_leave(v8_handle_scope_t* scope) {
  delete (HandleScope*)scope;
}

With carefully balanced usage in code that needs handle scopes:

for (i = 0; i < N; i++) {
  v8_handle_scope_t scope;
  v8_handle_scope_enter(&scope);
  // action
  v8_handle_scope_leave(&scope);
}
like image 62
Vyacheslav Egorov Avatar answered Nov 07 '22 19:11

Vyacheslav Egorov


As your question is about how to write V8 C++ addons for Node.js you should just refer to the manual for nodeJs which has a reasonably simple guide for writing addons;

If you question is about how to make background threads then there are nodejs plugins which helps you with that, but also read this for how to write it directly.

You should never try to access the memory for V8 directly, as the memory and object may move --always use the APIs

like image 27
Soren Avatar answered Nov 07 '22 19:11

Soren