fgerschau / comments

1 stars 0 forks source link

javascript-memory-management/ #10

Open utterances-bot opened 3 years ago

utterances-bot commented 3 years ago

JavaScript's Memory Management Explained

Even though the JavaScript engine manages memory for us, it's good to know what happens under the hood.

https://felixgerschau.com/javascript-memory-management/?ck_subscriber_id=478674400

LeeRob97 commented 3 years ago

Excellent article! I never exactly understood why objects were by reference, but why the properties inside (which I now know are primitive values) were not.

Do you have any articles on object destructuring?

EX:

let test = { name: "John" };

let test1 = { ...test }

Is javascript deferencing test in this example to create a new object?

fgerschau commented 3 years ago

@LeeRob97 thanks a lot! I've had a hard time figuring things like these out because this information is scattered across the internet.

Destructuring objects is the same as calling Object.assign({}, test), which creates a new object in the heap.

So in your example, test1 would hold a reference to a new object. test would still point to the same object.

If test had nested objects though, both objects test and test1 would point to the same nested object. That's because the spread operator and Object.assign only copy the values, like primitive values or references, not the object these values point to (that's called a shallow copy):

e.g:

const test = {
  name: 'John',
  dog: {
    name: 'Puppy',
  }
}

const test1 = { ...test }

test and test1 will point to the same dog object.

ancyrweb commented 3 years ago

Great article ! However i'm a bit confused by the use of stacks and heap. Mind you, i've been studying memory management at the OS level. I know what it's about, yet I feel like i'm missing something. The OS handles static and dynamic data into two areas, namely the Stack and the Heap. They grow in opposite directions. The Heap is named that way because all in all, it has no order whatsoever, hence the importance of keeping reference and manage freeing it. The Stack is named that way because it is a specific type of data-structure that goes LIFO, so if you want to get the 7th item from the top of the stack, you'll have to take out the 6 firsts and eventually put them back. But dealing with a lot of objects and arrays which contains primitive values would make the use of stack very slow since a lot of values would have to be popped off to fetch, say, the 30th or 40th item.

I think i've come through this question and had an answer, but i can't quite recall. Either there's actually optimized ways to walk through the stack, or there's actually a single name for 2 things : the Stack as an area for memory that is managed however the interpreter/OS wants to, and the Stack as a data-structure that the OS uses to manage said memory.

A refresher would be welcome if you have an idea.

fgerschau commented 3 years ago

Hey @rewieer, thanks for the feedback!

You're right, the stack and the heap have different ways of storing data.

Regarding the stack being LIFO: I believe that by this, people mean the order in which the functions will be processed, which is why it's also referred to as the call stack. The engine would execute the function on top of the stack first. I wrote about this in the first post of this series.

I found a stackoverflow answer where they explained which algorithms are used for storing data in the stack, but I can't find it anymore. If I find it I'll let you know, it basically said which algorithm is used to save and access the data in the "stack".

But yes, you're right and I think it's confusing to have the same name for multiple things: the call stack, the stack where JS saves primitive values, and the stack data-structure.

dwalker11 commented 3 years ago

This is a fantastic article. Thank you!

fgerschau commented 3 years ago

@dwalker11 thanks :)

mlavina commented 3 years ago

Great article one thing I would add is that in javascript if you want automatically trigger garbage collection on an array you can set the length to 0. That would probably be easier and more effective than doing the splice in your example.

Niproblema commented 3 years ago

Are you sure about stacks being static. As far as I remember from other languages, when calling methods, method frames are pushed onto stack, including it's scoped variables and all.

sandeshdamkondwar commented 3 years ago

Would be much helpful for others if you can summarize https://developers.google.com/web/tools/chrome-devtools/memory-problems in short.

fgerschau commented 3 years ago

@mlavina, thanks, you're right. I will update the article accordingly :)

@Niproblema, yes, it's static, which means that all values in the stack get assigned a fixed amount of memory, regardless of whether they will use all of it or not.

I think what you're referring to is the data structure of the stack. In the first article , I explain how this works more in detail. But you're right; the stack basically behaves as you described it.

@sandeshdamkondwar, I'll have a look. Maybe this would be a fit for another article :)

VladyslavGoloshchapov commented 3 years ago

thanks I think it would also be worth mentioning weakmap, weakset

fgerschau commented 3 years ago

@VladyslavGoloshchapov thanks for pointing that out. I wasn't aware of this memory leak; I'll look more into it and add it to the article :)

VladyslavGoloshchapov commented 3 years ago

these aren`t memory leaks. quite the opposite

fgerschau commented 3 years ago

Right, they solve memory leak issues with JS maps. I was looking at this stack overflow answer, which seems to explain it pretty well.

CianJS commented 3 years ago

Thank you for providing me with a fantastic article. Can I post it on my blog in my mother tongue?

fgerschau commented 3 years ago

@jskim-cian, sorry, I somehow missed your comment.

Sure go ahead :) Please send me the link when you're finished, so I can check it out

CianJS commented 3 years ago

I'll attach a link here. Please check!

fgerschau commented 3 years ago

Awesome, thanks! Can you please also link back to the article?

CianJS commented 3 years ago

Sorry. I saw it now. 😢 I wrote it down on the top, but can't you see it?

fgerschau commented 3 years ago

@JSKim-Cian no worries haha thanks again for the translation

rockerinnight commented 2 years ago

The 2-article series is great! It opened up my mind to JS engine a lot more. I have a question about reassign a new primitive value to a variable which is declared and assigned on above code. The JS interpreter will push new value to that "layer" in Stack or remove that layer and push a new layer of that variable with new value in?

HadeelAlwadia commented 2 years ago

I love it

eemanioui commented 1 year ago

Your last example snippet, which I copied below, will not delete all the DOM elements in the elements array, since you're directly mutating the elements array while iterating through it.

For each item you delete from elements using Array.prototype.splice, the order of the remaining items in elements will change, as they will be shifted to the left each time an item is deleted.

Here's an example to demonstrate it:

let items = [1,2,3,4,5,6,7,8,9];

function deleteItems() {
  items.forEach((item, index) => {
    items.splice(index, 1)
  })
}

// you can see here that not all of the elements were deleted
items // [2,4,6,8]

Here's what happened at the first 2 iterations, which will help explain the output above Iteration 1: index == 0 you delete the item at index 0(number 1), this will result in the items array being mutated to [2, 3, 4, 5, 6, 7, 8, 9] now the element that was at index 1 namely the number 2, is shifted left to take index position 0.

Iteration 2: Index == 1, at this point index == 1 is not number 2 because of the direct deletion you performed at iteration 1 Now index 1 points to 3, which gets deleted and as a result items array will look like [2, 4, 5, 6, 7, 8, 9]

// further iterations: same concept as the above 2 iterations above

The result is that every other item got deleted, but not all of them

Solutions:

  1. One solution would be to dereference the array object elements in your last example after you're done iterating over the elements.
  2. Another solution would be to use a while loop and delete each item that you're done working with until there are no more elements in the array I have included both solutions below.

This is the code example you shared

const elements = [];
const element = document.getElementById('button');
elements.push(element);

function removeAllElements() {
  elements.forEach((item, index) => {
    document.body.removeChild(document.getElementById(item.id));
    elements.splice(index, 1);
  });
}

Solution 1: dereferencing the elements array after you're done iterating over it

const elements = [];
const element = document.getElementById('button');
elements.push(element);

function removeAllElements() {
  elements.forEach((item, index) => {
    document.body.removeChild(document.getElementById(item.id));
  });
  elements = null; 
}

Solution 2: delete each element after you're done with until there are no more items left in the array

const elements = [];
const element = document.getElementById('button');
elements.push(element);

function removeAllElements() {
  let index = 0;
  while (elements.length !== 0) {
    let item = elements[index];
    document.body.removeChild(document.getElementById(item.id));
    elements.splice(index, 1);
  }
}
eemanioui commented 1 year ago

@fgerschau other than that, I'd say that the article is an excellent resource. I had bookmarked it for future reference.

eemanioui commented 1 year ago

@fgerschau I tried to subscribe to your newsletter using the bottom subscribe box on your homepage, and it resulted in an error.

Oops! An Error Occurred The server returned a "405 Method Not Allowed". Something is broken. Please let us know what you were doing when this error occurred. We will fix it as soon as possible. Sorry for any inconvenience caused.

I looked at the requests made from the network tab under chrome's developer tools, and it looks like you forgot to set the correct request method on your homepage's bottom subscribe form. As a result, when I submitted my email from it, it was sent as a GET request by default and not a POST request, which it should be.

I hope that helps!

larbi-ishak commented 1 year ago

Excellent article, Mark nd Sweep thats the core.

daiwanxing commented 1 year ago

It's Great article! I learned much about JS Memory Manage. And I want let more reader read about this artice. So Can I repost and translate your article to the Chinese technical forum ?😀

fgerschau commented 1 year ago

@daiwanxing sure! Please send me the link when you're done

daiwanxing commented 1 year ago

@daiwanxing sure! Please send me the link when you're done

no problem. Finally I have done, This is the translated article link: https://juejin.cn/post/7200956719978790967

fgerschau commented 1 year ago

Thanks, @daiwanxing! I added the links to your and @CianJS's translation to the article.

toanbui217 commented 1 year ago

Thanks for your article. This article helped me to understand memory in the JS engine. But i don't understand why, in the figure describing the Mark-and-sweep algorithm, the object 'dog' in the memory heap is considered a garbage even though it is still referenced by the object 'dog' in the memory stack?