如何获取JavaScript对象的大小?

我想知道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对象占用了多少内存?比如JavaScript中的sizeof() ?如果我能在像sizeof(stud)这样的单个函数调用中找到它,那就太棒了。

我已经在网上搜索了几个月了——没有找到它(在几个论坛上被问到——没有回复)。

372718 次浏览

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

在这条评论之后,你应该这样做: 尝试产生内存问题——编写代码来创建所有这些对象,并逐渐增加上限,直到遇到问题(浏览器崩溃,浏览器冻结或内存不足错误)。理想情况下,你应该在不同的浏览器和不同的操作系统上重复这个实验 现在有两个选项: 选项1 -您没有成功地产生内存问题。因此,你是在杞人忧天。你没有内存问题,你的程序很好

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

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

你能做的是尝试分配一个巨大的数量的对象,比如你想要的一百万或两百万对象。将对象放在一个数组中以防止垃圾收集器释放它们(注意,这将因为数组而增加轻微的内存开销,但我希望这无关紧要,此外,如果你担心对象在内存中,你应该将它们存储在某个地方)。在分配之前和之后添加一个警报,并在每个警报中检查Firefox进程占用了多少内存。在打开带有测试的页面之前,请确保您有一个新的Firefox实例。打开页面,注意“before”警告显示后的内存使用情况。关闭警报,等待分配内存。从旧内存中减去新的内存,然后除以分配的数量。例子:

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

我在我的计算机上尝试了这个方法,当“before”警告显示时,该进程有48352K的内存。分配之后,Firefox拥有440236K的内存。对于200万个分配,每个对象大约有200个字节。

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

所以,这里有一个可能对你有帮助的简单方法。JavaScript没有提供sizeof方法是有原因的:每个JavaScript实现都是不同的。以谷歌Chrome为例,相同的页面为每个对象使用大约66字节(至少从任务管理器判断)。

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

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

我写这个只是为了解决一个类似的问题。它不完全做你可能要找的,即它不考虑解释器如何存储对象。

但是,如果你正在使用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 );
}
对不起,我不能评论,所以我只能从明天开始继续工作。 此增强版本不会对对象进行多次计数,因此不会出现无限循环。 另外,我认为一个对象的键也应该被粗略地计算
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);
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;
}

我相信你忘了包括'array'。

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

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

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

谷歌Chrome堆分析器允许您检查对象内存使用。

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

在附件的截图中,我在窗口上创建了一个名为“testObj”的对象。然后我定位在分析器(在做了一个记录后),它显示了对象的完整大小和它在“保留大小”下的所有内容。

关于内存故障的更多细节

Chrome profiler

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

Chrome开发工具有这个功能。我发现这篇文章非常有帮助,它完全是你想要的: https://developers.google.com/chrome-developer-tools/docs/heap-profiling < / p >

非常感谢所有为此编写代码的人!

我只是想补充一点,我一直在寻找完全相同的东西,但在我的情况下,它是为了管理已处理对象的缓存,以避免不得不重新解析和处理来自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
},
};

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

我使用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>
在我的Chromium上,实例化200万个对象,每个对象只有一个属性(如上面的代码中所示)导致每个对象的粗略计算为50字节。更改代码为每个对象创建一个随机字符串会为每个对象增加大约30个字节,等等。

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

原文发表于此

有一个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));

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

否则,我建议使用Chrome/Firefox堆分析器。

有时我用它来标记非常大的对象,这些对象可能会从服务器发送到客户端。它不代表内存占用。它只会让你得到大约发送或存储它的成本。

还请注意,它很慢,仅用于开发。但是用一行代码就能得到大概的答案,这对我来说很有用。

roughObjSize = JSON.stringify(bigObject).length;

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

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

I 知道这绝对不是正确的方式来做它,但它已经帮助我在过去几次,以获得大致的对象文件大小:

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

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

重要的:

我使用了github上燕清共享的函数 https://gist.github.com/zensh/4975495 < / p >

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

你觉得怎么样?

在@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: 'John',
last_name: 'Doe',
born: new Date(1980, 1, 1),
favorite_foods: ['Pizza', 'Salad', 'Indian', 'Sushi'],
can_juggle: true
};


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

我与上面的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

参考:

接受的答案不适用于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一起工作。

在chrome中拍摄一个头部快照,并监视对象的大小: enter image description here < / p >

enter image description here