Pull to refresh

Comments 11

Разница между статическим и динамическим выделением памяти

Мне кажется, можно ограничиться тем, что статическая память известна на момент компиляции, а динамическая нет, иначе за слова «статическая память выделяется в стеке.» сейчас полетят метеориты в Ваш огород.
На аппаратном уровне компьютерная память состоит из множества триггеров.

Оперативная память современного ПК или сервера не состоит из триггеров — это было бы безумно дорого при таких объёмах. Сегодня в оперативной памяти информацию хранят конденсаторы, заряд которых периодически восстанавливается контроллером памяти, а SRAM (то, что на транзиторах) используется лишь в случаях, когда требуется действительно высокое быстродействие (например, кэш-память).

Меня всегда интересовало, насколько критично отсутствие unsubscribe.
Например, в Angular'овском http-клиенте:
this.http.get('/api/items');

Вернёт Observable на который можно подписаться и, по логике, нужно отписываться после использования.

В данном варианте, http.get('/api/items') генерирует только одно Observable значение, поэтому отписываться от него не нужно, RxJs сам позаботится об этом.

Если Observable завершается (onComplete или onError) unsubscribe не нужно делать не нужно, это происходит автоматически.
UFO just landed and posted this here
var theThing = null;
var replaceThing = function () {
  var originalThing = theThing;
  var unused = function () {
    if (originalThing) // ссылка на originalThing
      console.log("hi");
  };
  theThing = {
    longStr: new Array(1000000).join('*'),
    someMethod: function () {
      console.log("message");
    }
  };
};
setInterval(replaceThing, 1000);


Сейчас не везде данный пример приведет к утечке памяти, так как сборщики стали умные и анализируют код, смотрят функцию в unused, может ли она когда-либо быть вызвана, если нет, то чистят память.
Вот скрин полностью загруженной страницы, мы видим 40.8 MB
image

Запускаем пример в консоли(страница к этому моменту уже полностью загружена и ни какие больше процессы не проходят).
image
С течением времени как была цифра 41MB, так и осталось.
Мониторинг системы показывает несколько значений по вкладке, но регулярно возвращается к значению 315MB.
image

Я переписала код:
var theThing = null;

var replaceThing = function () {
  var originalThing = theThing;

  var unused = function () {
    if (originalThing) // ссылка на originalThing
      console.log(originalThing);
  };
  setInterval(unused, 1000);

  theThing = {
    longStr: new Array(1000000).join('*')
  };
};

setInterval(replaceThing, 1000);


Потребление памяти стало расти, так как функция точно стала использоваться:
image

Скрины мониторинга:
image
image

Если посмотреть по мониторингу, то все равно, у хрома после 500MB, значение не много уменьшается, вполне возможно, что все же сборщик мусора начинает что-то подозревать и все же чистит.

Не проверяла, но скорее всего фаерфокс не справится с такой утечкой.

Я решила посмотреть историю данного примера:
4 вида утечек памяти в JavaScript и как с ними бороться (2016). Здесь уже убраны фигурные скобки у if. Есть еще более ранняя статья https://habrahabr.ru/company/mailru/blog/233553/ (2014) Это перевод статьи: https://www.toptal.com/javascript/10-most-common-javascript-mistakes
Дальше в гугле показал, что пример прошел несколько раз через Stack Overflow.
Одно из самых ранних упоминания здесь — A surprising JavaScript memory leak found at Meteor (27.06.2013). В 2013 году конечно, такой код, без варинтов, вызвал бы утечку памяти.
если я правильно понимаю
setInterval(unused, 1000);

просто кладет unused в gc-root, а в примере шла речь об утечках через замыкания.
проблема отсутствия утечки кроется в том, что современный браузер выпиливает неиспользуемые переменные из замыканий. то есть если раньше все утекало через функцию someMethod, то теперь чтобы пример завелся, нужно явно указать unused в someMethod
var theThing = null;
var replaceThing = function () {
  var originalThing = theThing;
  var unused = function () {
    if (originalThing) // ссылка на originalThing
      console.log("hi");
  };
  theThing = {
    longStr: new Array(1000000).join('*'),
    someMethod: function () {
      console.log("message");
      unused; //ссылка на unused
    }
  };
};
setInterval(replaceThing, 1000);

но тогда unused становится не таким уж unused)
ну не знаю, я запустил на NodeJS 9 с интервалом 10 милисекунд и у меня выдало ошибку out of memory через меньше чем минуту.
На аппаратном уровне компьютерная память состоит из множества триггеров. Каждый триггер состоит из нескольких транзисторов, он способен хранить один бит данных. Каждый из триггеров имеет уникальный адрес, поэтому их содержимое можно считывать и перезаписывать. Таким образом, концептуально, мы можем воспринимать компьютерную память как огромный массив битов, которые можно считывать и записывать.

Однако, программисты — люди, а не компьютеры, оперировать отдельными битами им не особенно удобно. Поэтому биты принято организовывать в более крупные структуры, которые можно представлять в виде чисел. 8 бит формируют 1 байт. Помимо байтов здесь в ходу такое понятие, как слова (иногда — длиной 16 битов, иногда — 32).

Извините, но это абсолютно неверно.
Процессор не может адресовать конкретный бит. У процессора просто не такой команды как «запиши/прочитай бит номер N в памяти». Минимальной адресуемой единицей является байт. У каждого байта есть свой адрес и процессор может читать и писать по байту или больше.
В байте не обязательно восемь бит. Старые компьютеры работали с пяти- или шести- битными байтами. Т.е. у каждой пятерки (шестерки) бит был свой адрес. Сейчас существуют архитектуры, где байт имеет длину 32 бита (DSP всякие). Опять же, это значит что процессор не может прочитать или записать меньше 32 бит за раз. И у каждых 32 бит есть свой адрес.
По определению, байт — это минимальный независимо адресуемый набор данных.

А ровно восемь бит имеет октет. Вы наверное встречали это слово, когда работали с сетями. Дело в том, что через сеть могут быть соединены машины совершенно разных архитектур, с разной длиной байта. Поэтому, что бы не было путаницы, в сетевых протоколах используют слово «октет».

А со «словом» существует ещё большая путаница. Размер машинного слова — это размер регистра.
Раньше, в эпоху 16 битных компьютеров слово состояло из двух байт. Сейчас оно чаще всего состоит из восьми.

Я сталкивался за свою практику с утечкой в рекурсии.

Sign up to leave a comment.