如何获取JavaScript对象的大小?


296

我想知道一个JavaScript对象占用的大小。

具有以下功能:

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

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

现在我实例化student

var stud = new Student();

这样我就可以做

stud.firstName = "new Firstname";

alert(stud.firstName);

stud.marks.maxMarks = 200;

等等

现在,stud对象将在内存中占据一些大小。它具有一些数据和更多对象。

我如何找出stud对象占用多少内存?像sizeof()JavaScript中的?如果我能在单个函数调用中找到它,那将真的很棒sizeof(stud)

我已经在互联网上搜索了几个月了-找不到它(在几个论坛中提问-没有回复)。


41
有很多原因(为什么会出现),我需要在JavaScript中查找对象的大小。我是JavaScript的新手,所以我没有遵循最佳实践。我还在学习。我已经开发了一个firefox扩展程序,我确定它使用的内存超出了应有的数量。我正在研究一些方法(希望)减少内存使用。.我正在谈论每个选项卡上有数千个实例许多对象,因此内存确实很重要!问题是,我想知道我的内存减少工作是否真的有助于减少内存..以及减少了多少。

1
如果您构建了FF扩展,我建议您以比JS高的语言水平进行调查-看看对FF本身有什么影响。
annakata

2
语言比JS高?在这种情况下会是什么?

24
无论如何,大小都不重要。这就是你如何使用它。PS:多么棒!
Thomas Eding

13
@SpencerRuport知道对象大小的第一个原因是具有html5脱机功能的应用程序,在这些应用程序中您有时存储空间有限(对于数据密集型应用程序,可能会很快消耗5-10Mb)。
大卫,

Answers:


181

我已经在原始答案中重构了代码。我删除了递归并删除了假定的存在开销。

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;
}

35
您可能还想考虑对象键
zupa

8
任何登陆这里寻找最小类型的对象都是false / true,它似乎是undefined / null。
zupa

3
"よんもじ".length在Javascript中为4,但您确定它是8个字节,因为您的代码返回了它?
syockit

8
是的 JavaScript中的字符是根据ECMA-262第三版规范存储的-bclary.com/2004/11/07/#a-4.3.16
thomas-peter

4
此函数不计算隐藏在闭包中的引用。例如var a={n:1}; var b={a:function(){return a}}; roughSizeOfObject(b),此处b保留对的引用a,但roughSizeOfObject()返回0
罗曼·波米诺夫

116

Google Chrome堆分析器可让您检查对象内存的使用情况。

您需要能够在跟踪中找到对象,这可能很棘手。如果将对象固定到全局窗口,则很容易从“包含”列表模式中找到。

在所附的屏幕截图中,我在窗口上创建了一个名为“ testObj”的对象。然后,我位于探查器中(进行记录后),它显示对象的完整大小以及“保留大小”下的所有内容。

有关内存故障的更多详细信息

Chrome分析器

在上面的屏幕截图中,该对象显示的保留大小为60。我相信这里的单位是字节。


13
这个答案与以下问题一起解决了我的问题:developers.google.com/chrome-developer-tools/docs/…。快速提示:快速获取堆快照,运行您怀疑正在泄漏的任务,获取新的快速堆快照,然后选择comparison底部的视图。这使在两个快照之间创建了哪些对象变得显而易见。
约翰里德(Johnride)2014年

1
这可能是最干净的解决方案。
卡·斯蒂布

@Johnride提到的比较现在是顶部的下拉菜单。
frandroid

Shallow size{ a:"55c2067aee27593c03b7acbe", b:"55c2067aee27593c03b7acbe", c:null, d:undefined }和和两者均为40 { c:null, d:undefined }。可以吗
efkan

1
您也可以从节点使用Google Chrome Heap Profiler。如果您拥有节点v8或更高版本,请从中启动,node --inspect并在Chrome中输入about:inspectURL栏,然后寻找打开节点检查器。在节点CLI中创建对象,然后进行堆快照。
格雷戈尔

74

我只是写这篇文章来解决类似的问题。它并不能完全满足您的需求,即没有考虑解释器存储对象的方式。

但是,如果您使用的是V8,那么它应该可以给您一个很好的近似值,因为出色的原型设计和隐藏类占用了大部分开销。

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

1
@Liangliang Zheng-伟大的点无限循环,谢谢。希望您不介意我重新修改一下内容,然后下班后更新我的内容(?)
thomas-peter

我今晚将使用node.js进行基准测试,并获得一些更好的系数。
thomas-peter

'bytes + = recurse(value [i])'行在我的FF 14.01中引发错误:NS_ERROR_FAILURE:组件返回失败代码:0x80004005(NS_ERROR_FAILURE)[nsIDOMHTMLInputElement.selectionStart]。在我的一个对象上,如果我尝试使用其他对象,则不会,可能是浏览器错误,或者代码对每个对象都不起作用(一个不起作用的对象包含函数,一个不起作用的对象包含函数) t)
TrySpace 2012年

1
从一个汤姆到另一个汤姆,这对我需要在哪里修剪对象腰围的粗略指示非常有用。那您的小姐是否发现了您与Java的关系?她是个高维护的情妇。
汤姆W厅

4
我必须注意的是PHP(吞噬)-很久以前我就停止爱她了,但她付了账。无论如何,谢谢汤姆,喜欢的反馈比信誉点好。
thomas-peter

55

有时,我用它来标记可能从服务器发送到客户端的非常大的对象。它不代表内存占用量。它只是使您大致了解发送或存储它的成本。

还要注意,它很慢,仅适用于开发人员。但是对于用一行代码来获得一个标准答案来说,这对我来说很有用。

roughObjSize = JSON.stringify(bigObject).length;

2
根据我的测试,此方法比object-sizeof的情况要快得多,因为它没有lodash发出的_.isObject()慢速调用。另外,返回的大小与粗略估计值相当。要点gist.github.com/owenallenaz/ff77fc98081708146495
Nucleon


5
不能与圆形结构一起使用VM1409:1 Uncaught TypeError: Converting circular structure to JSON:(虽然仍然有用
givanse

这不是以字节为单位的二进制大小,而是易于使用以获得近似大小
daddinhquoc

如果1)您只需要一个大概的估计,2)您知道自己没有任何循环引用,那么3)可以忽略较大的值并将它们分别弄乱,这非常好。就我而言,所有这些都是正确的,因此可以完美地工作,我在一个地方只有一根大弦,可以用来测量长度。
OZZIE

43

这是一个稍微紧凑的解决方案:

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

3
这是KB大小吗?或位?
森特·索普

2
@ vincent-thorpe以字节为单位。

2
不错的脚本,不过需要修改循环引用。
Jim Pedid

1
我刚刚在节点进程中的大量数据上测试了您的算法,它报告的容量为13GB,但是节点消耗的容量为22GB,您知道差异的来源吗?存储器中没有其他东西了。
JosuGoñi18年

3
@JosuGoñi,他们不计算对象本身需要多少,仅计算其值。所有对象占用的空间不只是它们的值,否则typeof ...将无法工作。
亚历克西斯·威尔克

42

有一个NPM模块来获取对象sizeof,您可以使用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));

sizeof(new Date())=== 0和sizeof({})===0。这是故意的吗?
菲利普·克拉森(PhilippClaßen)'18年

1
@PhilippClaßen显然是。这两个对象都没有属性。
罗伯特

18

这是一种骇人听闻的方法,但是我用不同的数字尝试了两次,这似乎是一致的。

您可以做的是尝试分配大量对象,例如您想要的一或两百万个对象。将对象放在数组中以防止垃圾回收器释放它们(请注意,由于数组,这将增加一点内存开销,但是我希望这没有关系,此外,如果您要担心对象在内存中, ,则将它们存储在某个地方)。在分配之前和之后添加警报,并在每个警报中检查Firefox进程占用了多少内存。在打开带有测试的页面之前,请确保您具有新的Firefox实例。打开页面,注意显示“之前”警报后的内存使用情况。关闭警报,等待内存分配。从旧的内存中减去新的内存,然后将其除以分配量。

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

我在计算机上尝试了此操作,并且显示“之前”警报时,该进程的内存为48352K。分配后,Firefox拥有440236K的内存。对于200万个分配,每个对象大约200个字节。

我再次尝试了100万次分配,结果很相似:每个对象196个字节(我假设2mill中的额外数据用于数组)。

因此,这是一种可以帮助您的hacky方法。JavaScript不提供“ sizeof”方法是有原因的:每个JavaScript实现都不相同。例如,在Google Chrome浏览器中,同一页面为每个对象使用大约66个字节(至少从任务管理器判断)。


嘿..谢谢你的技术。如果没有直接方法来测量内存使用情况,我将其作为计划B。

4
每个C和C ++实现也都不同。;)C或C ++中数据类型的大小是特定于实现的。我认为没有理由JavaScript不支持这样的运算符,尽管它不能达到与C或C ++相同的目的或具有相同的含义(它们是较低级的语言,并且可以测量固定值的实际大小)与在运行时动态JavaScript对象的可变大小相反的大小)。
bambams

12

对不起,我无法发表评论,所以我从错误的角度继续工作。此增强版本不会对对象计数一次以上,因此不会造成无限循环。另外,我认为应该大致算出对象的键。

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

我认为这很准确,因为它在计数密钥,尽管它确实在计数'__visited__'密钥
Sam Hasler

检查typeof value === 'object'还不够,如果值为,您将遇到异常null
floribon 2014年

与@tomwrong的任何欺骗性答案相比,这对于我的对象来说是飞快的(我非常有信心超过5mb)。它也更准确(如它所说的3mb左右),但与现实相去甚远。关于它可能不算在内的任何线索?
cregox 2015年

对我不起作用。对象level包含数据,但roughSizeOfObject(level)返回零。(当然,不要将我的变量级别与您的参数混淆。我不认为变量阴影会在这里引起问题,而且当我在脚本中重命名“级别”时,也会得到相同的结果。) :snipboard.io/G7E5yj.jpg
Luc

10

有同样的问题。我在Google上进行了搜索,并希望与stackoverflow社区分享此解决方案。

重要事项

我使用了Yan Qing在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);

你怎么看待这件事?


3
这错过了功能。如果添加函数,则该对象将不会显示更大的值
Don Rhummy17年

不计算密钥
ebg11

7

我想知道我减少记忆的努力是否真的有助于减少记忆

在此评论之后,您应该执行以下操作:尝试产生内存问题-编写创建所有这些对象并逐渐增加上限的代码,直到遇到问题(浏览器崩溃,浏览器冻结或Out-Of-Of)。内存错误)。理想情况下,您应该使用不同的浏览器和不同的操作系统重复此实验。

现在有两个选项:选项1-您没有成功产生内存问题。因此,您什么都不担心。您没有内存问题,您的程序很好。

选项2-您确实遇到了内存问题。现在,问问自己出现问题的限制是否合理(换句话说:是否有可能在正常使用代码时创建此数量的对象)。如果答案为“否”,那么您就可以了。否则,您现在知道您的代码可以创建多少个对象。重做算法,使其不会超出此限制。


从内存的角度来看,我的扩展程序为在Firefox中打开的每个页面/选项卡添加了许多对象。“数量”与页面大小成正比。假设“高级”用户可以打开15-20个标签之间的任意位置,并且如果网页中包含大量内容,则浏览器将在一段时间后变得缓慢且令人沮丧且无响应。即使没有我明确尝试强调该应用程序,也会发生这种情况。我计划重写我认为会减少大量对象创建的代码。我只是想确保没有。减少的对象数量等于某物,这是值得的

@Senthil:但是对象大小没有意义,除非您知道可用内存量。由于内存量可能仍然是个谜,因此以#objects而言与以#bytes而言同样有用
Itay Maman


5

这个Javascript库sizeof.js做同样的事情。包括这样

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

sizeof函数将一个对象作为参数,并返回其近似大小(以字节为单位)。例如:

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

sizeof函数可以处理包含对其他对象的多个引用和递归引用的对象。

最初在这里发表


在我的用例中,这比@liangliang的速度慢,并且似乎“不够精确”。
cregox


2

非常感谢一直为此编写代码的每个人!

我只是想补充一点,我一直在寻找完全相同的东西,但就我而言,这是为了管理已处理对象的缓存,以避免必须从可能已缓存或未缓存的ajax调用中重新解析和处理对象通过浏览器。这对于需要大量处理的对象(通常不是JSON格式的任何对象)特别有用,但将这些内容缓存在大型项目或长时间运行的应用程序/扩展中可能会非常昂贵。时间。

无论如何,我将其用于类似:

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
    },
};

这是一个简单的示例,可能会有一些错误,但是它给出了这个主意,因为您可以使用它以某种程度的智能抓住静态对象(内容不会改变)。这可以大大降低必须首先生产物体的任何昂贵的加工要求。


1
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;
}

1

我使用Chrome开发人员工具的“ 时间轴”标签,实例化越来越多的对象,并获得类似的良好估算。您可以像下面这样使用html作为样板,并对其进行修改以更好地模拟对象的特征(属性的数量和类型等)。在运行之前和之后,您可能要单击该“开发工具”选项卡底部的垃圾位图标。

<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>

实例化200万个仅具有一个属性的对象(如上面的代码所示),导致在我的Chromium上,每个对象粗略地计算50个字节。更改代码以为每个对象创建随机字符串会为每个对象增加大约30个字节,以此类推。希望这会有所帮助。



-3

我相信您忘记了包含“数组”。

  typeOf : function(value) {
        var s = typeof value;
        if (s === 'object')
        {
            if (value)
            {
                if (typeof value.length === 'number' && !(value.propertyIsEnumerable('length')) && typeof value.splice === 'function')
                {
                    s = 'array';
                }
            }
            else
            {
                s = 'null';
            }
        }
        return s;
    },

   estimateSizeOfObject: function(value, level)
    {
        if(undefined === level)
            level = 0;

        var bytes = 0;

        if ('boolean' === typeOf(value))
            bytes = 4;
        else if ('string' === typeOf(value))
            bytes = value.length * 2;
        else if ('number' === typeOf(value))
            bytes = 8;
        else if ('object' === typeOf(value) || 'array' === typeOf(value))
        {
            for(var i in value)
            {
                bytes += i.length * 2;
                bytes+= 8; // an assumed existence overhead
                bytes+= estimateSizeOfObject(value[i], 1)
            }
        }
        return bytes;
    },

   formatByteSize : function(bytes)
    {
        if (bytes < 1024)
            return bytes + " bytes";
        else
        {
            var floatNum = bytes/1024;
            return floatNum.toFixed(2) + " kb";
        }
    },

1
在JS中,数组是一个对象。实现中可能会有一些优化,但是从概念上讲,数组和对象是相同的。
克里斯·沃克

-8

知道这绝对不是正确的方法,但是过去它已经帮助我几次来获得大约目标文件的大小:

将对象/响应写入控制台或新选项卡,将结果复制到新的记事本文件,保存并检查文件大小。记事本文件本身只有几个字节,因此您将获得相当准确的目标文件大小。


4
这是完全错误的。例如,考虑数字1/3 = 0.3333333333333333。使用您的方法将为18个字节。
korCZis

2
我说是大概的。有时您并不关心它是1MB还是1.00001MB,只想知道一个估计,那么这种方法就很好了。
杰弗里·罗森达尔

厚脸皮解决方案X]
Lapys
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.