minne - Hur får man storleken på ett JavaScript-objekt?


Jag vill veta vilken storlek som tas upp av ett JavaScript-objekt.

Ta följande funktion:

function Marks(){
  this.maxMarks = 100;
}

function Student(){
  this.firstName = "firstName";
  this.lastName = "lastName";
  this.marks = new Marks();
}

Nu instansierar jag student:

var stud = new Student();

så att jag kan göra saker som

stud.firstName = "new Firstname";

alert(stud.firstName);

stud.marks.maxMarks = 200;

osv.

Nu kommer stud-objektet att upptaga en viss storlek i minnet. Det har viss data och fler objekt.

Hur tar jag reda på hur mycket minne stud-objektet upptar? Något som sizeof() i JavaScript? Det vore verkligen fantastiskt om jag kunde ta reda på det med ett enda funktionsanrop som sizeof(stud).

Jag har letat på Internet i månader—kunde inte hitta det (frågade i några forum—inga svar).

Svar #1

Jag har omstrukturerat koden i mitt ursprungliga svar. Jag har tagit bort rekursionen och tagit bort antagandet om existens-överhuvudtaget. Detta är en ny redigering för att göra den lite mer uppdaterad i stil.

function roughSizeOfObject(object) {
  const objectList = [];
  const stack = [object];
  let bytes = 0;

  while (stack.length) {
    const value = stack.pop();

    switch (typeof value) {
      case 'boolean':
        bytes += 4;
        break;
      case 'string':
        bytes += value.length * 2;
        break;
      case 'number':
        bytes += 8;
        break;
      case 'object':
        if (!objectList.includes(value)) {
          objectList.push(value);
          for (const prop in value) {
            if (value.hasOwnProperty(prop)) {
              stack.push(value[prop]);
            }
          }
        }
        break;
    }
  }

  return bytes;
}

Svar #2

Google Chrome Heap Profiler låter dig inspektera objektets minnesanvändning.

Du behöver kunna lokalisera objektet i spåret vilket kan vara knepigt. Om du fäster objektet till fönstrets globala objekt är det ganska lätt att hitta från listningsläget "Containment".

I den bifogade skärmdumpen skapade jag ett objekt som heter "testObj" på fönstret. Jag lokaliserade det sedan i profileraren (efter att ha gjort en inspelning) och det visar objektets fulla storlek och allt i det under "behållen storlek".

Fler detaljer om minnesuppdelningarna.

I skärmdumpen ovan visar objektet en behållen storlek på 60. Jag tror att enheten är byte här.

Svar #3

Ibland använder jag detta för att markera riktigt stora objekt som kan skickas till klienten från servern. Det representerar inte minnesavtrycket. Det ger dig bara ungefär vad det skulle kosta att skicka det eller lagra det.

Notera också, det är långsamt, endast för utvecklare. Men för att få ett ungefärligt svar med en rad kod har det varit användbart för mig.

roughObjSize = JSON.stringify(bigObject).length;

Svar #4

Jag skrev precis detta för att lösa ett liknande (ungefär) problem. Det gör inte exakt vad du kanske letar efter, det vill säga det tar inte hänsyn till hur tolken lagrar objektet.

Men om du använder V8 bör det ge dig en ganska bra approximation eftersom den fantastiska prototypningen och de dolda klasserna tar upp det mesta av overheaden.

function roughSizeOfObject( object ) {

    var objectList = [];

    var recurse = function( value )
    {
        var bytes = 0;

        if ( typeof value === 'boolean' ) {
            bytes = 4;
        }
        else if ( typeof value === 'string' ) {
            bytes = value.length * 2;
        }
        else if ( typeof value === 'number' ) {
            bytes = 8;
        }
        else if
        (
            typeof value === 'object'
            && objectList.indexOf( value ) === -1
        )
        {
            objectList[ objectList.length ] = value;

            for( i in value ) {
                bytes+= 8; // an assumed existence overhead
                bytes+= recurse( value[i] )
            }
        }

        return bytes;
    }

    return recurse( object );
}

Svar #5

Här är en något mer kompakt lösning på problemet:

const typeSizes = {
  "undefined": () => 0,
  "boolean": () => 4,
  "number": () => 8,
  "string": item => 2 * item.length,
  "object": item => !item ? 0 : Object
    .keys(item)
    .reduce((total, key) => sizeOf(key) + sizeOf(item[key]) + total, 0)
};

const sizeOf = value => typeSizes[typeof value](value);

Svar #6

Det finns ett NPM-modul för att få objektets storlek, du kan installera det med npm install object-sizeof

  var sizeof = require('object-sizeof');

  // 2B per character, 6 chars total => 12B
  console.log(sizeof({abc: 'def'}));

  // 8B for Number => 8B
  console.log(sizeof(12345));

  var param = { 
    'a': 1, 
    'b': 2, 
    'c': {
      'd': 4
    }
  };
  // 4 one two-bytes char strings and 3 eighth-bytes numbers => 32B
  console.log(sizeof(param));

Svar #7

Det här är en kladdig metod, men jag provade den två gånger med olika nummer och det verkar vara konsekvent.

Vad du kan göra är att försöka allokera ett stort antal objekt, som en eller två miljoner objekt av den typ du vill ha. Lägg objekten i en array för att förhindra att garbage collectorn frigör dem (observera att detta kommer att lägga till en liten minneskostnad på grund av arrayen, men jag hoppas att detta inte spelar någon roll och dessutom, om du ska oroa dig för att objekt är i minnet, måste du lagra dem någonstans). Lägg till en alert före och efter allokeringen och i varje alert kontrollera hur mycket minne Firefox-processen använder. Innan du öppnar sidan med testet, se till att du har en ny Firefox-instans. Öppna sidan, notera minnesanvändningen efter att "före"-alerten visas. Stäng alerten, vänta tills minnet är allokerat. Subtrahera det nya minnet från det äldre och dela det med antalet allokeringar. Exempel:

function Marks()
{
  this.maxMarks = 100;
}

function Student()
{
  this.firstName = "firstName";
  this.lastName = "lastName";
  this.marks = new Marks();
}

var manyObjects = new Array();
alert('before');
for (var i=0; i<2000000; i++)
    manyObjects[i] = new Student();
alert('after');

Jag testade detta på min dator och processen hade 48352K minne när "före"-varningen visades. Efter tilldelningen hade Firefox 440236K minne. För 2 miljoner tilldelningar är detta ungefär 200 byte för varje objekt.

Jag försökte igen med 1 miljon allokeringar och resultatet var liknande: 196 byte per objekt (jag antar att de extra datan i 2 miljoner användes för Array).

Så, här är en klumpig metod som kanske kan hjälpa dig. JavaScript tillhandahåller inte en "sizeof"-metod av en anledning: varje JavaScript-implementation är olika. I Google Chrome till exempel använder samma sida ungefär 66 byte för varje objekt (åtminstone baserat på Aktivitetshanteraren).

Svar #8

Förlåt att jag inte kunde kommentera, så jag fortsatte bara arbetet från tomwrong. Denna förbättrade version kommer inte räkna objekt mer än en gång, alltså ingen oändlig loop. Dessutom tror jag att nyckeln till ett objekt också borde räknas, ungefär.

function roughSizeOfObject( value, level ) {
    if(level == undefined) level = 0;
    var bytes = 0;

    if ( typeof value === 'boolean' ) {
        bytes = 4;
    }
    else if ( typeof value === 'string' ) {
        bytes = value.length * 2;
    }
    else if ( typeof value === 'number' ) {
        bytes = 8;
    }
    else if ( typeof value === 'object' ) {
        if(value['__visited__']) return 0;
        value['__visited__'] = 1;
        for( i in value ) {
            bytes += i.length * 2;
            bytes+= 8; // an assumed existence overhead
            bytes+= roughSizeOfObject( value[i], 1 )
        }
    }

    if(level == 0){
        clear__visited__(value);
    }
    return bytes;
}

function clear__visited__(value){
    if(typeof value == 'object'){
        delete value['__visited__'];
        for(var i in value){
            clear__visited__(value[i]);
        }
    }
}

roughSizeOfObject(a);

Svar #9

Har samma problem. Jag sökte på Google och jag vill dela med stackoverflow-communityn denna lösning.

Viktigt:

Jag använde funktionen som delades av Yan Qing på GitHub https://gist.github.com/zensh/4975495

function memorySizeOf(obj) {
    var bytes = 0;

    function sizeOf(obj) {
        if(obj !== null && obj !== undefined) {
            switch(typeof obj) {
            case 'number':
                bytes += 8;
                break;
            case 'string':
                bytes += obj.length * 2;
                break;
            case 'boolean':
                bytes += 4;
                break;
            case 'object':
                var objClass = Object.prototype.toString.call(obj).slice(8, -1);
                if(objClass === 'Object' || objClass === 'Array') {
                    for(var key in obj) {
                        if(!obj.hasOwnProperty(key)) continue;
                        sizeOf(obj[key]);
                    }
                } else bytes += obj.toString().length * 2;
                break;
            }
        }
        return bytes;
    };

    function formatByteSize(bytes) {
        if(bytes < 1024) return bytes + " bytes";
        else if(bytes < 1048576) return(bytes / 1024).toFixed(3) + " KiB";
        else if(bytes < 1073741824) return(bytes / 1048576).toFixed(3) + " MiB";
        else return(bytes / 1073741824).toFixed(3) + " GiB";
    };

    return formatByteSize(sizeOf(obj));
};


var sizeOfStudentObject = memorySizeOf({Student: {firstName: 'firstName', lastName: 'lastName', marks: 10}});
console.log(sizeOfStudentObject);

Vad tycker du om det?

Relaterade artiklar