2012-04-02 9 views
5

Zawsze byłem przy założeniu, że klucze obiektu były przechowywane jako łańcuchy i że każda nie-łańcuchowa wartość byłaby rzutowana. Tak, to był pod tym założeniu, podczas pisania kodu, który miał przechowywać niewielką wartość dla wielu tysięcy kluczy, ja konwertowane wszystkie klucze do oparcia 36:Jak są nazwy atrybutów obiektów przechowywanych w JavaScript?

// theKey is an integer 
myMap[theKey.toString(36)] = theValue; 

Potem postanowiliśmy sprawdzić, czy moje założenie był poprawny i użył profilera Chrome, aby sprawdzić wykorzystanie pamięci. Grubsza tutaj są testy I pobiegł i użycie pamięci:

window.objIntegers = {}; 
for (i = 100000; i--) window.objIntegers[i] = 'a'; 
// 786kb 

window.objStrings = {}; 
for (i = 100000; i--) window.objStrings[i.toString(36)] = 'a'; 
// 16.7mb! 

// and the same pattern but with: 
key = i + .5; // 16.7mb 
key = i + ''; // 786kb 
key = '0' + i; // 16.7mb 
key = i + '0'; // 16.7mb 

Oczywiście, moje założenia były wyłączone. Zastanawiam się jednak, w jaki sposób są przechowywane i czy to zachowanie jest standardowe, czy tylko dodatkowe oszustwo, które zostało dodane przez zespół Chromium/WebKit?

Odpowiedz

0

Optymalizacja w Chromium. Uważam, że ma heurystykę (here's one mention of it) w celu określenia najbardziej wydajnego sposobu przechowywania właściwości wewnętrznie. Wszystko, co określa specyfikacja ECMAScript, to interfejs między JavaScriptem a środowiskiem i nie mówi nic o tym, jak obiekty wystawione na JavaScript są zaimplementowane wewnętrznie.

3

To naprawdę dodatkowe oszustwo V8.

JSObject (wewnętrzna C++ przedstawieniem JS Object) ma dwie cechy, elements i properties, gdzie „elementy” są atrybuty JS ze wskaźników liczbowych, podczas gdy „Properties” są atrybuty JS z indeksami łańcuchowych.

Oczywiście, indeksy numeryczne zużywają tutaj znacznie mniej pamięci, ponieważ nazwy właściwości nie muszą być przechowywane.

http://code.google.com/intl/de-DE/chrome/devtools/docs/memory-analysis-101.html#primitive_objects

Typowy obiekt JavaScript posesses dwie tablice: jedna do przechowywania nazwanych właściwości, inny do przechowywania elementów liczbowych.

Widać to z kodu źródłowego v8:

http://code.google.com/p/v8/source/browse/trunk/src/objects.h#1483

// [properties]: Backing storage for properties. 
... 
// [elements]: The elements (properties with names that are integers). 

http://code.google.com/p/v8/source/browse/trunk/src/runtime.cc#4462

MaybeObject* Runtime::SetObjectProperty(Isolate* isolate, 
             Handle<Object> object, 
             Handle<Object> key, 
             Handle<Object> value, 
             PropertyAttributes attr, 
             StrictModeFlag strict_mode) { 
    ... 

    // Check if the given key is an array index. 
    uint32_t index; 
    if (key->ToArrayIndex(&index)) { 
    // In Firefox/SpiderMonkey, Safari and Opera you can access the characters 
    // of a string using [] notation. We need to support this too in 
    // JavaScript. 
    // In the case of a String object we just need to redirect the assignment to 
    // the underlying string if the index is in range. Since the underlying 
    // string does nothing with the assignment then we can ignore such 
    // assignments. 
    if (js_object->IsStringObjectWithCharacterAt(index)) { 
     return *value; 
    } 

    Handle<Object> result = JSObject::SetElement(
     js_object, index, value, attr, strict_mode, set_mode); 
    if (result.is_null()) return Failure::Exception(); 
    return *value; 
    } 

    if (key->IsString()) { 
    Handle<Object> result; 
    if (Handle<String>::cast(key)->AsArrayIndex(&index)) { 
     result = JSObject::SetElement(
      js_object, index, value, attr, strict_mode, set_mode); 
    } else { 
     Handle<String> key_string = Handle<String>::cast(key); 
     key_string->TryFlatten(); 
     result = JSReceiver::SetProperty(
      js_object, key_string, value, attr, strict_mode); 
    } 
    if (result.is_null()) return Failure::Exception(); 
    return *value; 
    } 

    // Call-back into JavaScript to convert the key to a string. 
    bool has_pending_exception = false; 
    Handle<Object> converted = Execution::ToString(key, &has_pending_exception); 
    if (has_pending_exception) return Failure::Exception(); 
    Handle<String> name = Handle<String>::cast(converted); 

    if (name->AsArrayIndex(&index)) { 
    return js_object->SetElement(
     index, *value, attr, strict_mode, true, set_mode); 
    } else { 
    return js_object->SetProperty(*name, *value, attr, strict_mode); 
    } 
} 

Nie będę wchodzić w szczegóły, ale należy pamiętać, że SetObjectProperty połączeń albo SetElement lub SetProperty, w zależności od klawisza. Nie wiem jednak, dlaczego test nie powiedzie się w teście przypadku: key = i + '0'.

Powiązane problemy