Как получить размер объекта 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
объект занимает? Что-то вроде sizeof()
в JavaScript? Было бы действительно здорово, если бы я мог найти это в одном вызове функции, как sizeof(stud)
,
Я искал в Интернете в течение нескольких месяцев - не мог найти его (спросили на нескольких форумах - без ответов).
22 ответа
Я пересмотрел код в моем первоначальном ответе. Я удалил рекурсию и удалил предполагаемые накладные расходы на существование.
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;
}
Google Chrome Heap Profiler позволяет вам контролировать использование памяти объекта.
Вы должны быть в состоянии найти объект в трассировке, что может быть сложно. Если вы прикрепите объект к глобальному окну, его будет легко найти в режиме списка "Containment".
На прикрепленном скриншоте я создал объект с именем "testObj" в окне. Затем я расположился в профилировщике (после создания записи), и он показывает полный размер объекта и все, что в нем находится в "сохраненном размере".
На приведенном выше снимке экрана объект показывает оставшийся размер 60. Я полагаю, что единица измерения - это байты.
Иногда я использую это для пометки действительно больших объектов, которые могут отправляться клиенту с сервера. Это не представляет в памяти след. Он просто дает вам примерно столько, сколько стоило бы отправить его или сохранить.
Также обратите внимание, что это медленно, только для разработчиков. Но для получения приблизительного ответа с одной строкой кода это было полезно для меня.
roughObjSize = JSON.stringify(bigObject).length;
Я просто написал это, чтобы решить аналогичную проблему. Это не совсем то, что вы можете искать, то есть не учитывает, как интерпретатор хранит объект.
Но, если вы используете 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 );
}
Немного опоздал на вечеринку, но вот чуть более компактное решение проблемы:
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);
Существует модуль NPM, чтобы получить размер объекта, вы можете установить его с 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));
Это хакерский метод, но я попробовал его дважды с разными числами, и он, кажется, соответствует.
Что вы можете сделать, это попытаться выделить огромное количество объектов, например, один или два миллиона объектов того типа, который вам нужен. Поместите объекты в массив, чтобы сборщик мусора не освободил их (обратите внимание, что это приведет к небольшим накладным расходам памяти из-за массива, но я надеюсь, что это не должно иметь значения, кроме того, если вы будете беспокоиться о том, что объекты находятся в памяти храните их где-нибудь). Добавьте предупреждение до и после выделения и в каждом предупреждении проверьте, сколько памяти занимает процесс 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');
Я попробовал это на своем компьютере, и у процесса было 48352 КБ памяти, когда было показано предупреждение "до". После выделения Firefox имел 440236 КБ памяти. Для 2 миллионов выделений это около 200 байтов для каждого объекта.
Я попробовал это снова с распределением в 1 миллион, и результат был похож: 196 байтов на объект (я полагаю, что дополнительные данные в 2mill использовались для массива).
Итак, вот хакерский метод, который может вам помочь. JavaScript не предоставляет метод "sizeof" по причине: каждая реализация JavaScript отличается. Например, в Google Chrome эта же страница использует около 66 байт для каждого объекта (судя по крайней мере из диспетчера задач).
Извините, я не смог прокомментировать, поэтому я просто продолжаю работу от 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);
Имея ту же проблему. Я искал в Google и хочу поделиться с сообществом stackru этим решением.
Важно:
Я использовал функцию, которой поделился Ян Цин, на 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);
Что вы думаете об этом?
я хочу знать, действительно ли мои усилия по уменьшению памяти помогают уменьшить память
Следуя этому комментарию, вот что вы должны сделать: Попробуйте создать проблему с памятью - напишите код, который создает все эти объекты и постепенно увеличивает верхний предел до тех пор, пока вы не столкнулись с проблемой (сбой браузера, зависание браузера или выход из строя). ошибка памяти). В идеале вы должны повторить этот эксперимент с разными браузерами и разными операционными системами.
Теперь есть два варианта: вариант 1 - вам не удалось создать проблему с памятью. Следовательно, вы беспокоитесь ни за что. У вас нет проблем с памятью, и ваша программа в порядке.
вариант 2- у вас проблемы с памятью. Теперь спросите себя, является ли предел, при котором возникла проблема, разумным (другими словами: вероятно, что это количество объектов будет создано при нормальном использовании вашего кода). Если ответ "Нет", то все в порядке. В противном случае вы теперь знаете, сколько объектов может создать ваш код. Переработайте алгоритм так, чтобы он не нарушал этот предел.
Эта библиотека 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 может обрабатывать объекты, которые содержат несколько ссылок на другие объекты и рекурсивные ссылки.
Инструменты разработчика Chrome обладают этой функциональностью. Я нашел эту статью очень полезной и делает именно то, что вы хотите: https://developers.google.com/chrome-developer-tools/docs/heap-profiling
Если вас больше всего беспокоит использование памяти расширением Firefox, я советую проверить с разработчиками Mozilla.
Mozilla предоставляет в своей вики список инструментов для анализа утечек памяти.
Принятый ответ не работает сMap
,
Set
,
WeakMap
и другие итерируемые объекты. (Пакет 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
.
Большое спасибо всем, кто работал над кодом для этого!
Я просто хотел добавить, что я искал точно такую же вещь, но в моем случае это для управления кэшем обработанных объектов, чтобы избежать необходимости повторного анализа и обработки объектов из вызовов 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
},
};
Это упрощенный пример и может иметь некоторые ошибки, но он дает идею, поскольку вы можете использовать его для удержания статических объектов (содержимое не изменится) с некоторой степенью интеллекта. Это может значительно сократить любые дорогостоящие требования к обработке, которые объект должен был быть произведен в первую очередь.
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;
}
Я использую вкладку " Временная шкала" инструментов Chrome dev, создаю все более и более крупные объекты и получаю такие хорошие оценки. Вы можете использовать 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>
Создание 2 миллионов объектов только с одним свойством каждый (как в этом коде выше) приводит к грубому вычислению 50 байтов на объект, на моем Chromium, прямо сейчас. Изменение кода для создания случайной строки для каждого объекта добавляет около 30 байтов для каждого объекта и т. Д. Надеюсь, это поможет.
Если вам нужно программно проверить на aprox. Размер объектов вы также можете проверить эту библиотеку http://code.stephenmorley.org/javascript/finding-the-memory-usage-of-objects/ которую я смог использовать для размера объектов.
В противном случае я предлагаю использовать Chrome/Firefox Heap Profiler.
Основываясь на уже компактном решении от @Dan, вот его автономная функциональная версия. Имена переменных сокращены до одинарных букв для тех, кто просто хочет, чтобы они были как можно более компактными за счет контекста.
У меня были проблемы с приведенным выше ответом с
ArrayBuffer
. После проверки документации я обнаружил, что ArrayBuffer имеет
byteLength
свойство, которое точно сообщает мне, что мне нужно, следовательно:
Ссылка:
Я полагаю, что вы забыли включить "массив".
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";
}
},
Я знаю, что это абсолютно неправильный способ сделать это, но в прошлом мне уже несколько раз приходилось получать приблизительный размер объектного файла:
Запишите ваш объект / ответ на консоль или новую вкладку, скопируйте результаты в новый файл блокнота, сохраните его и проверьте размер файла. Сам файл блокнота занимает всего несколько байтов, поэтому вы получите довольно точный размер объектного файла.