Сколько вкладок браузера займет ваша оперативная память? Почему браузер получает так много оперативной памяти? Можно ли уменьшить количество потребляемой памяти? В этой статье я постараюсь ответить на все эти вопросы.

История

Если вы знакомы с JS, то должны знать, что в JS нет free или malloc подобных функций. Память выделяется динамически, а затем освобождается сборщиком мусора, когда это необходимо. Некоторые разработчики могут писать алгоритмы, неудобные для памяти, поэтому некоторые сайты занимают так много памяти. Есть ли способ сделать это лучше? Да! Следуй за мной, я покажу тебе дорогу.

Поймите основы

Прежде всего, давайте узнаем, сколько памяти занимают некоторые примитивные типы. Здесь я не рассматриваю типы null , undefined , symbol .

Переменная number занимает 8 байт, согласно международному стандарту IEEE 754.

Переменная A string зависит от длины строки: один символ занимает 2 байта.

Переменная boolean… Вот это хитрая штука. Дело в том, что размер действительно зависит от базовой среды (под платформой я имею в виду JS-движок, который реализует управление памятью). Но в основном это может быть 1 байт или 4 байта.

Как насчет объектов?

Размер объекта вычислить сложно, поскольку он зависит от ключей и значений, содержащихся в объекте. Часто объекты могут содержать вложенные объекты, поэтому задача усложняется. Более того, нам нужно учитывать тот факт, что каждый движок Javascript реализует управление памятью по-разному. Из-за этого размер объекта может отличаться в зависимости от браузера. Например, профилировщик кучи Google Chrome имеет два разных столбца для размера объекта: поверхностный размер и сохраненный размер.

Неглубокий размер — это объем памяти, занимаемой самим объектом.

Оставшийся размер – это объем памяти, который освобождается после удаления самого объекта вместе с зависимыми от него объектами, которые стали недоступны из корневых каталогов GC.

Дополнительную информацию о Google Chrome Heap Profiler и управлении памятью вы можете найти здесь.

Бывают случаи, когда нам не нужен этот сумасшедший объем памяти. Или, может быть, мы хотим хранить информацию в битах, поэтому нам не нужно выделять 8 байтов на число. Что мы можем сделать?

Используйте типизированные массивы!

В Javascript есть массивоподобные объекты, которые позволяют работать с двоичными данными. Вы можете просто создать их следующим образом:

// This buffer has a size of 8 bytes
const buffer = new ArrayBuffer(8);
console.log(buffer.byteLength) // 8

Дело в том, что buffer не Array. Вы не можете получить доступ к элементам с помощью индексов и сделать много вещей, связанных с массивами. Что вы можете с этим сделать?

Чтобы получить доступ к данным буфера, вам нужно создать своего рода представление поверх данных. Например, вы можете рассматривать этот буфер как массив UInt8 или массив UInt16 следующим образом:

const view = new Uint8Array(buffer);
console.log(view[0]); // 0
console.log(Array.isArray(view)); // false

Просто помните, что view тоже не Array.

Вы можете сделать следующие представления:

  • Int8Array
  • Uint8Array
  • Uint8ClampedArray
  • Int16Array
  • Uint16Array
  • Int32Array
  • Uint32Array
  • Float32Array
  • Float64Array
  • BigInt64Array
  • BigUint64Array

Интересно то, что для одного буфера можно сделать несколько представлений:

const buffer = new ArrayBuffer(8);
const uint8view = new Uint8Array(buffer);
const uint16view = new Uint16Array(buffer);
uint8view[2] = 6;
uint16view[0] = 5;
console.log(uint8view); // Uint8Array(8) [5, 0, 6, 0, 0, 0, 0, 0]
console.log(uint16view); // Uint16Array(4) [5, 6, 0, 0]

Будьте осторожны, потому что вы меняете один и тот же буфер памяти.

Упаковка данных

В некоторых случаях вам нужно обрабатывать множество сложных структур данных (например, объекты с множеством разных полей). В целях экономии памяти их можно упаковать в ArrayBuffer . Например, у вас есть следующий объект (этот пример для языка C):

struct someStruct {
  unsigned long id;
  char username[16];
  float amountDue;
};

Эту структуру данных довольно легко упаковать в ArrayBuffer.

// First of all you need to allocate the memory for the object
const buffer = new ArrayBuffer(24);
// Then you can place your data to the buffer
// Here the second argument is the offset within the buffer
// The third argument is the number of the data view units you want to use
const idView = new Uint32Array(buffer, 0, 1);
const usernameView = new Uint8Array(buffer, 4, 16);
const amountDueView = new Float32Array(buffer, 20, 1);

Легко создавать собственные структуры данных и более эффективно использовать память.

Вывод

Полезно знать об ArrayBuffer и типизированных массивах. Они могут помочь уменьшить использование памяти вашим приложением. Тем не менее, есть случаи, когда ArrayBuffer пригодится: Web Workers. Вы можете передать буфер массива напрямую веб-воркеру, не копируя данные! Впрочем, это уже другая история…