Ladies and gentlemen, kasha Town Building

preface

I believe all of you have encountered such a scene: we give a page to make a beautiful click effect, for example, click anywhere on the page to create a gradually spreading ripple, spread to a certain size and then disappear. In js, we can add a click event to the window, create a row element (say span element) and mount it to the body, then use setTimeout and element.remove delay to clear the span object. Finally, add a little style and animation to the corresponding element to complete the effect. But is the SPAN tag object really eliminated?

Animation to achieve

<style>
  body {
    margin: 0;
    padding: 0;
    background-color: rgb(255.122.228);
  }

  span {
    position: fixed;
    border-radius: 50%;
    width: 40px;
    height: 40px;
    transform: translate(-50%, -50%);
    background-color: rgb(0.162.255);
    animation: animation 2s linear infinite;
  }

  @keyframes animation {
    0% {
      width: 0px;
      height: 0px;
      opacity: 0.5;
    }

    100% {
      width: 16vw;
      height: 16vw;
      opacity: 0;
    }
  }
</style>

<body id="body">
  <script>
     let body = document.getElementById('body')
    window.addEventListener('click'.e= > {
      let x = e.clientX
      let y = e.clientY

      let eleSpan = document.createElement('span')
      eleSpan.style.left = `${x}px`
      eleSpan.style.top = `${y}px`

      body.appendChild(eleSpan)
      
      setTimeout(() = > {
        eleSpan.remove()
        console.log(eleSpan);
      }, 2000);
    })
  </script>
</body>
Copy the code

I printed eleSpan after eleSpan. Remove. Here’s a try:

You can clearly see that I clicked twice on the page, and after a two-second delay on each click, the console printedspanLabel object, this showseleSpan.removeJust tospanThe labelDOMRemoved from the node, not removed from memory.

Hazards and solutions

In the example above, I only clicked on the page twice, but if you get thousands of hits every day for your future projects, you’ll be stuck with thousands of these SPAN TAB objects in memory. EleSpan = null; eleSpan = null; All I can say is, the big guy is the big guy, and sure enough, I’m teaching a fish how to swim. And that’s exactly how it works.

  setTimeout(() = > {
    span.outerHTML = null
    span = null
    console.log(span);
  }, 2000);
Copy the code

When you set eleSpan to NULL, you not only remove the node, but manually implement a garbage collection. OuterHTML must be null before eleSpan is set to NULL. If eleSpan is set to NULL, the SPAN element on the DOM node will still exist. I don’t know the specific reason. Welcome to comment!

When the SPAN element is referenced by another object

Before the span element is reclaimed, I create a new object storage, set the SPAN tag object to the value of its attribute DOM, and mount storage.dom as a node to the body as follows:

  let storage = {
     dom: eleSpan
  }
  
  body.appendChild(storage.dom)

  let str = 'Print storage.dom:'
  setTimeout(() = > {
    eleSpan.outerHTML = null
    eleSpan = null
    console.log('Print eleSpan:${eleSpan}`);
    console.log(str, storage.dom);
  }, 2000);
Copy the code

Let’s see what happens

Oh ho, onlyeleSpanIt was recycled,storage.domStill exists in memory, that is, in this case, manual memory reclamation failed. Why is that? Here’s why:

The principle of

A reference to an object is a count reference. When a reference type is created, the count of the number of times it is referenced in memory is increased by one. In the above example, the span tag object was already increased by one when it was created, and it was later increased by one when it was referenced by storage.dom, so the total count is 2. And then there were one.

How to solve

Dom is set to null and span’s reference count is reduced by one.

Js storage and garbage collection mechanisms

The storage mechanism

There are two types of memory space in JavaScript, stack memory and heap memory. Stack memory is relatively small, generally used to store simple data types, can quickly complete the switch of execution stack; The heap memory is large and complex data types are stored in the heap memory. When we assign a complex data type to a variable, such as var obj = {}, JavaScript does the following:

  • Declare a stack memory namedobjThe variables of
  • Create a new memory space storage in heap memory{}
  • willobjPointer to heap memory{}Storage space

Obj then creates a strong reference to the memory space of {}. If obj is assigned null, the strong reference relationship is removed. Dom = eleSpan. Dom refers to the span label object’s storage space in the heap memory. The SPAN label object eleSpan and Storagr. dom are kept in reference by two variables. When span = null is executed, only eleSpan is removed from the span label object in the heap memory storage space, and the storagr.dom reference relationship is not affected.

Garbage collection mechanism

In fact, JavaScript has automatic memory reclamation mechanism, but due to the special nature of reference data types and the existence of closures, this automatic reclamation mechanism is not reliable. In general, once a simple data type is called, it is destroyed in the stack memory space, and its life cycle is prolonged in the closure. Reference data is only reclaimed when its reference count in memory reaches zero, or when all references to it have been removed.

Recycling is important

Garbage collection is important for performance optimization because space on any device is limited and precious, and any optimization of memory should do garbage collection to free memory in a timely manner.

Manually debugging memory

In node.js, you can manually debug memory by using commands to observe memory overhead. Open the terminal in any file and run the following command:

1. Node — expose-GC enters interactive mode

2. Process.memoryusage () displays the memoryUsage

HeapUsed indicates that the heap space is used.

3.global.gc () performs a garbage collection manually, where global is the global object in Node.js.

So let’s do a little test in interactive mode:

process.memoryUsage()
/* Output: {RSS: 26640384, heapTotal: 4792320, heapUsed: 4125200, external: 1722368, arrayBuffers: 59073} */

let arr = new Array(5 * 1024 * 1024) 
process.memoryUsage() 
/* Output: {RSS: 68145152, heapTotal: 47525888, heapUsed: 45540456, external: 1722410, arrayBuffers: 75459} */
Copy the code

As you can see, the initial heapUsed value was 4,125,200. When I declared a 5MB array, the value of heapUsed became 45,540,456, which is more than ten times the initial value. Continue testing:

arr = null
global.gc()
process.memoryUsage()
{
  rss: 26234880.heapTotal: 5054464.heapUsed: 3291664.external: 1721811.arrayBuffers: 132797} * /Copy the code

As you can see, after setting arR to NULL and performing a garbage collection on global.gc(), memory is freed and heapUsed is back to its original size. Once you’ve mastered this technique, you can perform some more complex tests to see how you can completely free up memory while keeping multiple references to the arR.

WeakMap

As you can see from the above, garbage collection of a reference data type that holds multiple references is a complex and difficult task, which makes memory performance optimization unfriendly. The good news is that Giant’s glasses are also smart. JavaScript developers have introduced some new data structures in ED6, including one called WeakMap that addresses garbage collection. WeakMap is a special Map structure. Compared with Map, WeakMap has the following characteristics:

  • WeakMapIs not enumerable
  • WeakMapThe key must be a complex data type
  • WeakMapObjects that are not referenced by the key are automatically garbage collected

Reason: WeakMap key has weak reference relationship to other objects. When the object pointed to by the key is recovered in other places, WeakMap will automatically remove the reference relationship with the original object. Let’s do a test in interactive mode (details about entering interactive mode can be found in the manual debug memory summary) :

let key = new Array(5 * 1024 * 1024)
let map = new WeakMap()
map.set(key, 1)

process.memoryUsage()
/* Output: {RSS: 68268032, heapTotal: 47001600, heapUsed: 45424824, // 8 bits external: 1721770, arrayBuffers: 157372} */

key=null
global.gc()
process.memoryUsage()

/* Output: {RSS: 26259456, heapTotal: 5054464, heapUsed: 3308112, // 7-bit external: 1721864, arrayBuffers: 280262} */
Copy the code

From the overhead of the above code, the memory of the array in the heap is reclaimed after executing key = null. It is not reclaimed because map keeps referencing it. This is why the WeakMap key keeps weak reference relationships: Weak reference relationships do not increase the reference count of the referenced object in heap memory by one compared to strong reference relationships.

WeakMap extension

WeakMap implements private variables

WeakMap can also be used as a way to implement private variables, with the following code:

const privateData = new WeakMap(a)class Person {
  constructor(name, age) {
    privateData.set(this, { name: name, age: age })
  }
  getName() {
    return privateData.get(this).name
  }
  getAge() {
    return privateData.get(this).age
  }
}


const kaSha = new Person('card Sally'.18) 
console.log(kaSha.name);  // undefined
console.log(kaSha.getName()); / / card Sally
console.log(kaSha.getAge()); / / 18
Copy the code

Of course, using Map can also achieve the effect, but the Map key is to maintain a strong reference relationship, using Map may cause the problem of automatic memory reclamation, resulting in information leakage, so it is not recommended to use Map.

conclusion

JavaScript often fails to automatically reclaim memory due to the special nature of reference data type, which makes us often have to manually reclaim the memory of reference data, which may be very difficult. Therefore, in ES6 specification, JS provides WeakMap to automatically reclaim memory of reference data. Of course, WeakMap does more than that. It also has other functions, such as implementing private variables

* Writing articles is not easy, you big guy point a praise and then go 🥳😄👍