web-dev-qa-db-fra.com

Comment obtenir la taille d'un objet JavaScript?

Je veux connaître la taille occupée par un objet JavaScript.

Prenez la fonction suivante:

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

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

Maintenant j'instancie la student:

var stud = new Student();

afin que je puisse faire des choses comme

stud.firstName = "new Firstname";

alert(stud.firstName);

stud.marks.maxMarks = 200;

etc.

Maintenant, l'objet stud occupera une certaine taille en mémoire. Il a des données et plus d'objets.

Comment connaître la quantité de mémoire occupée par l'objet stud? Quelque chose comme une sizeof() en JavaScript? Ce serait vraiment génial si je pouvais le trouver dans un seul appel de fonction comme sizeof(stud).

Cela fait des mois que je fais des recherches sur Internet - je n’ai pas pu le trouver (question posée dans quelques forums - pas de réponse).

256
anonymous

J'ai re-factorisé le code dans mon réponse originale . J'ai supprimé la récursion et supprimé la surcharge de l'existence supposée.

function roughSizeOfObject( object ) {

    var objectList = [];
    var stack = [ object ];
    var bytes = 0;

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

        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.Push( value );

            for( var i in value ) {
                stack.Push( value[ i ] );
            }
        }
    }
    return bytes;
}
158
thomas-peter

Google Chrome Heap Profiler vous permet de contrôler l'utilisation de la mémoire des objets.

Vous devez être capable de localiser l'objet dans la trace, ce qui peut être délicat. Si vous épinglez l'objet à la fenêtre globale, il est assez facile à trouver à partir du mode de liste "Confinement".

Dans la capture d'écran ci-jointe, j'ai créé un objet appelé "testObj" sur la fenêtre. Je me suis ensuite localisé dans le profileur (après avoir fait un enregistrement) et il montre la taille complète de l'objet et tout ce qu'il contient sous "taille conservée".

Plus de détails sur les pannes de mémoire .

Chrome profiler

Dans la capture d'écran ci-dessus, l'objet montre une taille conservée de 60. Je crois que l'unité est en octets ici.

99
Guy Bedford

Je viens d'écrire ceci pour résoudre un problème similaire (ish). Il ne fait pas exactement ce que vous recherchez, c'est-à-dire qu'il ne prend pas en compte la manière dont l'interprète stocke l'objet.

Mais, si vous utilisez V8, cela devrait vous donner une approximation assez correcte, car le génial prototypage et les classes cachées génèrent l'essentiel des frais généraux.

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 );
}
69
thomas-peter

Parfois, je l'utilise pour signaler des objets très volumineux susceptibles d'être envoyés au client par le serveur. Cela ne représente pas l'empreinte en mémoire. Cela vous donne à peu près ce qu'il en coûterait pour l'envoyer ou le stocker.

Notez aussi que c'est lent, seulement pour les développeurs. Mais pour obtenir une réponse approximative avec une ligne de code, cela m'a été utile.

roughObjSize = JSON.stringify(bigObject).length;
47
zstew

Il y a un module NPM pour obtenir l'objet sizeof , vous pouvez l'installer avec 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));
38

Un peu tard pour la fête, mais voici une solution légèrement plus compacte au problème:

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);
34
Dan

Ceci est une méthode de hacky, mais je l'ai essayé deux fois avec des nombres différents et il semble être cohérent.

Ce que vous pouvez faire est d'essayer d'attribuer un nombre énorme d'objets, par exemple un ou deux millions d'objets du type souhaité. Placez les objets dans un tableau pour empêcher le ramasse-miettes de les libérer (notez que cela ajoutera une légère surcharge de mémoire à cause du tableau, mais j'espère que cela ne devrait pas avoir d'importance et que si vous allez vous inquiéter des objets en mémoire , vous les stockez quelque part). Ajoutez une alerte avant et après l'allocation et vérifiez dans chaque alerte la quantité de mémoire utilisée par le processus Firefox. Avant d'ouvrir la page avec le test, assurez-vous de disposer d'une nouvelle instance de Firefox. Ouvrez la page, notez l'utilisation de la mémoire après l'affichage de l'alerte "avant". Fermez l'alerte, attendez que la mémoire soit allouée. Soustrayez la nouvelle mémoire de l'ancienne et divisez-la par le nombre d'allocations. Exemple:

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');

J'ai essayé ceci dans mon ordinateur et le processus avait 48352K de mémoire lorsque l'alerte "avant" était affichée. Après l’allocation, Firefox disposait de 440236K de mémoire. Pour les affectations de 2 millions, cela représente environ 200 octets pour chaque objet.

Je l'ai essayé à nouveau avec des affectations de 1 million et le résultat était similaire: 196 octets par objet (je suppose que les données supplémentaires dans 2mill ont été utilisées pour Array).

Donc, voici une méthode de hacky qui pourrait vous aider. JavaScript ne fournit pas de méthode "sizeof" pour une raison: chaque implémentation de JavaScript est différente. Dans Google Chrome, par exemple, la même page utilise environ 66 octets pour chaque objet (à en juger par le gestionnaire de tâches au moins).

17
Bad Sector

Désolé de ne pas pouvoir commenter, je continue donc le travail de demain. Cette version améliorée ne comptera pas objet plus d'une fois, donc pas de boucle infinie. De plus, je pense que la clé d'un objet devrait également être comptée, à peu près.

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);
11
Liangliang Zheng

Avoir le même problème. J'ai cherché sur Google et je souhaite partager cette solution avec la communauté stackoverflow.

Important :

J'ai utilisé la fonction partagée par Yan Qing sur 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);

Qu'est-ce que tu en penses?

9
Ala Eddine JEBALI

je veux savoir si mes efforts de réduction de la mémoire aident réellement à réduire la mémoire

Pour faire suite à ce commentaire, voici ce que vous devez faire: Essayez de créer un problème de mémoire - Écrivez un code qui crée tous ces objets et augmente brutalement la limite supérieure jusqu’à ce que vous rencontriez un problème erreur de mémoire). Idéalement, vous devriez répéter cette expérience avec différents navigateurs et différents systèmes d'exploitation.

Il existe maintenant deux options: option 1 - Vous n’avez pas réussi à générer le problème de mémoire. Par conséquent, vous vous inquiétez pour rien. Vous n'avez pas de problème de mémoire et votre programme fonctionne bien.

option 2- vous avez eu un problème de mémoire. Maintenant, demandez-vous si la limite à laquelle le problème est survenu est raisonnable (en d’autres termes: est-il probable que cette quantité d’objets sera créée lors de l’utilisation normale de votre code)? Si la réponse est "non", alors ça va. Sinon, vous savez maintenant combien d'objets votre code peut créer. Retravaillez l'algorithme de manière à ne pas dépasser cette limite.

6
Itay Maman

Cette bibliothèque Javascript sizeof.js fait la même chose. Incluez-le comme ça

<script type="text/javascript" src="sizeof.js"></script>

La fonction sizeof prend un objet en paramètre et renvoie sa taille approximative en octets. Par exemple:

// define an object
var object =
    {
      'boolean' : true,
      'number'  : 1,
      'string'  : 'a',
      'array'   : [1, 2, 3]
    };

// determine the size of the object
var size = sizeof(object);

La fonction sizeof peut gérer des objets contenant plusieurs références à d'autres objets et références récursives.

Initialement publié ici .

5
Jayram

Si votre principale préoccupation concerne l'utilisation de la mémoire de votre extension Firefox, je suggère de vérifier auprès des développeurs de Mozilla.

Mozilla fournit sur son wiki un liste d'outils permettant d'analyser les fuites de mémoire .

4
Vincent Robert

Les outils de développement Chrome disposent de cette fonctionnalité. J'ai trouvé cet article très utile et fait exactement ce que vous voulez: https://developers.google.com/chrome-developer-tools/docs/heap-profiling

4
benja2729
function sizeOf(parent_data, size)
{
    for (var prop in parent_data)
    {
        let value = parent_data[prop];

        if (typeof value === 'boolean')
        {
            size += 4;
        }
        else if (typeof value === 'string')
        {
            size += value.length * 2;
        }
        else if (typeof value === 'number')
        {
             size += 8;
        }
        else
        {      
            let oldSize = size;
            size += sizeOf(value, oldSize) - oldSize;
        }
    }

    return size;
}


function roughSizeOfObject(object)
{   
    let size = 0;
    for each (let prop in object)
    {    
        size += sizeOf(prop, 0);
    } // for..
    return size;
}
2
Boris Rayskiy

Un grand merci à tous ceux qui ont travaillé sur le code pour cela!

Je voulais juste ajouter que je recherchais exactement la même chose, mais dans mon cas, c'est pour gérer un cache d'objets traités afin d'éviter d'avoir à réanalyser et à traiter les objets d'appels ajax qui ont peut-être été mis en cache ou non. par le navigateur. Ceci est particulièrement utile pour les objets nécessitant beaucoup de traitement, généralement tout ce qui n’est pas au format JSON, mais il peut être très coûteux de garder ces éléments en cache dans un grand projet ou dans une application/extension laissée en marche pendant longtemps. temps.

Quoi qu'il en soit, je l'utilise pour quelque chose comme:

var myCache = {
    cache: {},
    order: [],
    size: 0,
    maxSize: 2 * 1024 * 1024, // 2mb

    add: function(key, object) {
        // Otherwise add new object
        var size = this.getObjectSize(object);
        if (size > this.maxSize) return; // Can't store this object

        var total = this.size + size;

        // Check for existing entry, as replacing it will free up space
        if (typeof(this.cache[key]) !== 'undefined') {
            for (var i = 0; i < this.order.length; ++i) {
                var entry = this.order[i];
                if (entry.key === key) {
                    total -= entry.size;
                    this.order.splice(i, 1);
                    break;
                }
            }
        }

        while (total > this.maxSize) {
            var entry = this.order.shift();
            delete this.cache[entry.key];
            total -= entry.size;
        }

        this.cache[key] = object;
        this.order.Push({ size: size, key: key });
        this.size = total;
    },

    get: function(key) {
        var value = this.cache[key];
        if (typeof(value) !== 'undefined') { // Return this key for longer
            for (var i = 0; i < this.order.length; ++i) {
                var entry = this.order[i];
                if (entry.key === key) {
                    this.order.splice(i, 1);
                    this.order.Push(entry);
                    break;
                }
            }
        }
        return value;
    },

    getObjectSize: function(object) {
        // Code from above estimating functions
    },
};

C'est un exemple simpliste et peut comporter des erreurs, mais il en donne l'idée, car vous pouvez l'utiliser pour conserver des objets statiques (le contenu ne changera pas) avec une certaine intelligence. Cela peut réduire de manière significative les exigences de traitement coûteuses selon lesquelles l'objet devait être produit en premier lieu.

2
Haravikk

J'utilise Chrome dev tools ' onglet Timeline , instancie de plus en plus d'objets et obtenez de bonnes estimations de ce type. Vous pouvez utiliser un html comme celui ci-dessous, comme passe-partout, et le modifier pour mieux simuler les caractéristiques de vos objets (nombre et types de propriétés, etc.). Vous voudrez peut-être cliquer sur l'icône de la corbeille au bas de l'onglet Outils de développement, avant et après une analyse.

<html>
<script>
var size = 1000*100
window.onload = function() {
  document.getElementById("quantifier").value = size
}

function scaffold()
{
  console.log("processing Scaffold...");
  a = new Array
}

function start()
{
  size = document.getElementById("quantifier").value
  console.log("Starting... quantifier is " + size);
  console.log("starting test")
  for (i=0; i<size; i++){
    a[i]={"some" : "thing"}
  }
  console.log("done...")
}

function tearDown()
{
  console.log("processing teardown");
  a.length=0
}

</script>
<body>
    <span style="color:green;">Quantifier:</span>
    <input id="quantifier" style="color:green;" type="text"></input>
    <button onclick="scaffold()">Scaffold</button>
    <button onclick="start()">Start</button>
    <button onclick="tearDown()">Clean</button>
    <br/>
</body>
</html>

L'instanciation de 2 millions d'objets d'une seule propriété chacun (comme dans ce code ci-dessus) conduit à un calcul approximatif de 50 octets par objet, sur mon chrome, pour le moment. Changer le code pour créer une chaîne aléatoire par objet ajoute environ 30 octets par objet, etc. J'espère que cela vous aidera.

1
matanster

Si vous avez besoin de vérifier par programme pour aprox. taille des objets, vous pouvez aussi vérifier cette bibliothèque http://code.stephenmorley.org/javascript/finding-the-memory-usage-of-objects/ que j'ai pu utiliser pour la taille des objets .

Sinon, je suggère d'utiliser le Profiler Heap Chrome/Firefox.

1
Alex M