384

我想知道 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 中的 a 之类的东西?如果我能在像sizeof(stud).

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

4

22 回答 22

213

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

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;
}
于 2012-08-10T10:55:02.963 回答
148

Google Chrome 堆分析器允许您检查对象内存使用情况。

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

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

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

铬分析器

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

于 2012-09-13T14:28:22.503 回答
81

I just wrote this to solve a similar (ish) problem. It doesn't exactly do what you may be looking for, ie it doesn't take into account how the interpreter stores the object.

But, if you are using V8, it should give you a fairly ok approximation as the awesome prototyping and hidden classes lick up most of the overhead.

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 );
}
于 2011-06-14T23:30:53.723 回答
74

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

另请注意,它很慢,仅限开发人员。但是为了用一行代码得到一个大概的答案,这对我很有用。

roughObjSize = JSON.stringify(bigObject).length;
于 2015-01-28T05:42:06.010 回答
69

这是该问题的稍微更紧凑的解决方案:

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);
于 2015-04-01T09:32:22.007 回答
55

有一个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));
于 2014-05-18T11:46:49.413 回答
18

这是一个 hacky 方法,但我用不同的数字尝试了两次,它似乎是一致的。

您可以做的是尝试分配大量对象,例如您想要的一两百万个对象。将对象放入数组中以防止垃圾收集器释放它们(请注意,由于数组,这会增加一点内存开销,但我希望这无关紧要,此外,如果您担心对象在内存中,您将它们存储在某个地方)。在分配之前和之后添加一个警报,并在每个警报中检查 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 中的额外数据用于 Array)。

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

于 2009-08-08T09:57:04.097 回答
12

抱歉,我无法发表评论,所以我只是从 tomwrong 继续工作。此增强版本不会多次计算对象,因此没有无限循环。另外,我认为对象的键也应该粗略计算。

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);
于 2011-06-16T06:03:01.810 回答
12

有同样的问题。我在谷歌上搜索,我想与 stackoverflow 社区分享这个解决方案。

重要

我使用了严青在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);

你怎么看待这件事?

于 2017-01-08T10:44:12.350 回答
7

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

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

现在有两个选项:选项 1 - 您没有成功解决内存问题。因此,您无需担心。你没有内存问题,你的程序很好。

选项2-您确实遇到了内存问题。现在问问自己问题发生的限制是否合理(换句话说:在正常使用您的代码时是否可能会创建这么多的对象)。如果答案是“否”,那么你很好。否则,您现在知道您的代码可以创建多少个对象。重新设计算法,使其不违反此限制。

于 2009-08-08T09:34:35.907 回答
7

这个 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 函数可以处理包含对其他对象的多个引用和递归引用的对象。

最初在这里发布

于 2014-05-13T06:47:25.673 回答
5

如果您主要关心的是 Firefox 扩展的内存使用情况,我建议您咨询 Mozilla 开发人员。

Mozilla 在其 wiki 上提供了一个分析内存泄漏的工具列表

于 2010-01-09T11:50:41.120 回答
5

Chrome 开发者工具有这个功能。我发现这篇文章很有帮助,并且完全符合您的要求: https ://developers.google.com/chrome-developer-tools/docs/heap-profiling

于 2013-01-07T17:45:54.387 回答
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
    },
};

这是一个简单的例子,可能有一些错误,但它给出了这个想法,因为您可以使用它以某种程度的智能来保持静态对象(内容不会改变)。这可以显着减少首先必须生产对象的任何昂贵的处理要求。

于 2013-04-06T11:54:18.877 回答
1

我使用 Chrome 开发工具的Timeline 选项卡,实例化越来越多的对象,并获得类似的良好估计。您可以使用下面这样的 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>

现在,在我的 Chromium 上,实例化 200 万个对象,每个对象只有一个属性(如上面的代码所示),粗略计算每个对象需要 50 个字节。更改代码以创建每个对象的随机字符串会为每个对象添加大约 30 个字节等。希望这会有所帮助。

于 2013-12-11T23:51:24.263 回答
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;
}
于 2011-10-12T21:06:38.110 回答
1

如果您需要以编程方式检查 aprox. 对象的大小您还可以检查这个库http://code.stephenmorley.org/javascript/finding-the-memory-usage-of-objects/我已经能够用于对象大小。

否则我建议使用 Chrome/Firefox Heap Profiler。

于 2014-06-26T12:50:51.730 回答
1

我对上述答案有疑问ArrayBuffer。检查文档后,我发现 ArrayBuffer 有一个byteLength属性可以准确地告诉我我需要什么,因此:

function sizeOf(data)
{
    if (typeof(data) === 'object')
    {
        if (data instanceof ArrayBuffer)
        {
            return data.byteLength;
        }
        // other objects goes here
    }
    // non-object cases goes here
}

console.log(sizeOf(new ArrayBuffer(15))); // 15

参考:

于 2021-10-13T06:23:17.337 回答
1

在 @Dan 已经很紧凑的解决方案的基础上,这里有一个独立的函数版本。对于那些只希望它尽可能紧凑而牺牲上下文的人来说,变量名称被简化为单个字母。

const ns = {};
ns.sizeof = function(v) {
  let f = ns.sizeof, //this needs to match the name of the function itself, since arguments.callee.name is defunct
    o = {
      "undefined": () => 0,
      "boolean": () => 4,
      "number": () => 8,
      "string": i => 2 * i.length,
      "object": i => !i ? 0 : Object
        .keys(i)
        .reduce((t, k) => f(k) + f(i[k]) + t, 0)
    };
  return o[typeof v](v);
};

ns.undef;
ns.bool = true;
ns.num = 1;
ns.string = "Hello";
ns.obj = {
  first_name: 'Brendan',
  last_name: 'Eich',
  born: new Date(1961, 6, 4),
  contributions: ['Netscape', 'JavaScript', 'Brave', 'BAT'],
  politically_safe: false
};

console.log(ns.sizeof(ns.undef));
console.log(ns.sizeof(ns.bool));
console.log(ns.sizeof(ns.num));
console.log(ns.sizeof(ns.string));
console.log(ns.sizeof(ns.obj));
console.log(ns.sizeof(ns.obj.contributions));

于 2021-01-23T08:26:21.573 回答
0

接受的答案不适用于MapSetWeakMap其他可迭代对象。(其他答案中提到的包object-sizeof也有同样的问题)。

这是我的修复

export function roughSizeOfObject(object) {
  const objectList = [];
  const stack = [object];
  const bytes = [0];
  while (stack.length) {
    const value = stack.pop();
    if (value == null) bytes[0] += 4;
    else if (typeof value === 'boolean') bytes[0] += 4;
    else if (typeof value === 'string') bytes[0] += value.length * 2;
    else if (typeof value === 'number') bytes[0] += 8;
    else if (typeof value === 'object' && objectList.indexOf(value) === -1) {
      objectList.push(value);
      if (typeof value.byteLength === 'number') bytes[0] += value.byteLength;
      else if (value[Symbol.iterator]) {
        // eslint-disable-next-line no-restricted-syntax
        for (const v of value) stack.push(v);
      } else {
        Object.keys(value).forEach(k => { 
           bytes[0] += k.length * 2; stack.push(value[k]);
        });
      }
    }
  }
  return bytes[0];
}

它还包括其他一些小的改进:计算密钥存储并与ArrayBuffer.

于 2022-02-14T17:08:23.773 回答
-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";
        }
    },
于 2011-11-04T19:26:59.373 回答
-7

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

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

于 2016-03-27T21:32:28.347 回答