search
HomeWeb Front-endJS TutorialHow to use the garbage collector

How to use the garbage collector

May 30, 2018 am 10:11 AM
useRecycleRubbish

This time I will show you how to use the garbage collector and what are the precautions for using the garbage collector. The following is a practical case, let's take a look.

The garbage collector is a double-edged sword. The advantage is that it can greatly simplify the memory management code of the program, because memory management does not require programmers to operate, thereby reducing (but not eradicating) memory leaks in long-running programs. For some programmers, it can even improve the performance of their code.

On the other hand, choosing a garbage collector means that the program cannot fully control the memory, and this is the crux of mobile terminal development. For

JavaScript, there is no possibility of memory management in the program - the ECMAScript standard does not expose any garbage collector interface. Web applications have no way to manage memory or prompt the garbage collector.

Strictly speaking, languages ​​that use garbage collectors are not necessarily better or worse in performance than languages ​​that do not use garbage collectors. In

C language, allocating and releasing memory may be very expensive operations. In order to enable the allocated memory to be released in the future, heap management will tend to be complicated. In managed memory languages, allocating memory often just adds a pointer. But then we will see the huge cost of the garbage collector stepping in to collect when the memory is exhausted. An unexplored garbage collector will cause long and unpredictable pauses in the running of the program, which directly affects the experience of using interactive systems (especially those with animation effects). Reference counting systems are often touted as an alternative to garbage collection, but they can also cause unexpected pauses when the last object in a large subgraph is dereferenced. Moreover, the reference counting system will also have considerable performance burden when frequently performing read, rewrite, and store operations.

For better or worse, JavaScript needs a garbage collector. V8's garbage collector implementation is now mature, with excellent performance, short pauses, and very controllable performance burden.

Basic concepts

The most basic problem that the garbage collector needs to solve is to identify the memory that needs to be recycled. Once identified, these memory areas can be reused in future allocations or returned to the operating system. An object is dead (nonsense) when it is not active. An object is active if and only if it is pointed to by a root object or another active object. The root object is defined as the active object and is the object referenced by the browser or V8. For example, objects pointed to by local variables belong to the root object because their stack is considered the root object; global objects belong to the root object because they are always accessible; browser objects, such as DOM elements, also belong to the root object. Although in some cases they are only weak references.

From the side, the above definition is very loose. In fact we can say that an object is active when it can be referenced by a program. For example:

function f() {
	 var obj = {x: 12};
	 g(); // 可能包含一个死循环
	 return obj.x;
	}
def scavenge():
	 swap(fromSpace, toSpace)
	 allocationPtr = toSpace.bottom
	 scanPtr = toSpace.bottom
	 for i = 0..len(roots):
	 root = roots[i]
	 if inFromSpace(root):
	  rootCopy = copyObject(&allocationPtr, root)
	  setForwardingAddress(root, rootCopy)
	  roots[i] = rootCopy
	 while scanPtr During the execution of this algorithm, we always maintain two pointers in the out area: allocationPtr points to the place where we are about to allocate memory for a new object, and scanPtr points to the next one where we are about to perform an active check. object. The objects before the address pointed to by scanPtr are processed objects. They and their neighbors are all in the out area, and their pointers have been updated. The objects between scanPtr and allocationPtr will be copied to the out area, but within these objects If the contained pointers point to objects in the zone, these objects in the zone will not be copied. Logically, you can think of the objects between scanPtr and allocationPtr as a queue of objects used for breadth-first search. <p style="text-align: left;"></p>Translation Note: In breadth-first search, nodes are usually taken out from the head of the queue and expanded, and the expanded child nodes are stored at the end of the queue, and the cycle continues. This process is similar to updating an object between two pointers. <p style="text-align: left;"></p><p style="text-align: left;">At the beginning of the algorithm, we copy all objects in the new area that can be reached from the root object, and then enter a large loop. On each iteration of the loop, we remove an object from the queue, increment scanPtr, and then track the pointer accessing the object. If the pointer does not point to the entry area, ignore it, because it must point to the old area, and this is not our goal. And if the pointer points to an object in the incoming area, but we have not copied it (the forwarding address has not been set), then copy this <a href="http://www.php.cn/wiki/208.html" target="_blank"> object </a> to the outgoing area, that is, add it to the end of our queue, and at the same time, to allocationPtr increment. At this time, we will also store a forwarding address to the first word of the out-zone object and replace the Map pointer. This forwarding address is the address where the object is stored after being copied. The garbage collector can easily distinguish the forwarding address from the Map pointer because the Map pointer is marked, but this address is not. If we find a pointer and the object it points to has been copied (the forwarding address has been set), we update the pointer to the forwarding address and mark it. </p><p style="text-align: left;">The algorithm terminates when all objects have been processed (that is, scanPtr and allocationPtr meet). At this time, the content entered into the zone can be regarded as garbage and may be released or reused in the future. </p><p style="text-align: left;"><strong>Secret Weapon: Write Barrier</strong></p><p style="text-align: left;">One detail was ignored above: if an object in the new area has only one pointer to it, and this pointer happens to Among the objects in the old student area, how can we know which object in the new student area is active? Obviously we don't want to traverse the old raw area again, because there are many objects in the old raw area, and doing so once will consume too much. </p><p style="text-align: left;">In order to solve this problem, there is actually a list in the write buffer, which records all the objects in the old area pointing to the new area. When a new object is born, there will be no pointer to it. When an object in the old area has a pointer to an object in the new area, we will record such a cross-area pointer. Since this logging behavior always occurs during a write operation, it is called a write barrier—because every write operation must go through such a barrier. </p><p style="text-align: left;">You may be curious, if every write operation has to go through the write barrier, wouldn't there be a lot of extra code? Yes, this is one of the costs of our garbage collection mechanism. But the situation is not as serious as you think. After all, there are fewer write operations than read operations. Some garbage collection algorithms (not V8) will use read barriers, which require hardware assistance to ensure a lower cost. V8 also has some optimizations to reduce the consumption caused by write barriers: </p><p style="text-align: left;">Most of the script execution time occurs in Crankshaft, and Crankshaft can often statically determine whether an object is in the new area. Write operations to these objects do not require a write barrier. </p><p style="text-align: left;">There is a new optimization in Crankshaft, that is, when the object does not have a non-local reference pointing to it, the object will be allocated on the stack. The write operation related to an object on the stack obviously does not require a write barrier. (Annotation: The new student area and the old student area are on the heap.) </p><p style="text-align: left;">The situation of "old → new" is relatively rare, so by combining the two common situations of "new → new" and "old → old" Code optimization can relatively improve performance in most situations. Each page is aligned with 1MB, so given the memory address of an object, the page where it is located can be quickly located by filtering out the lower 20 bits; and the page header has relevant identification to indicate whether it belongs to the new area or the old area, so by Determining the area to which two objects belong can also quickly determine whether they are "old → new". </p><p style="text-align: left;">Once we find the "old → new" pointer, we can record it at the end of the write buffer. After a certain amount of time (when the write buffer is full), we sort it, merge identical items, and then remove pointers that no longer fit the "old → new" situation. (Annotation: In this way, the number of pointers will be reduced, and the time to write the barrier will be shortened accordingly) </p><p style="text-align: left;">"Mark-Clear" algorithm and "Mark-Compact" algorithm</p><p style="text-align: left;">Scavenge algorithm is for fast recycling , Compressing small pieces of memory works well, but it consumes too much for large pieces of memory. Because the Scavenge algorithm requires two areas, the outbound area and the inbound area, this is acceptable for small pieces of memory, but becomes impractical for memory exceeding a few MB. The old student area contains hundreds of MB of data. For such a large area, we adopt two other algorithms that are relatively close to each other: the "mark-clear" algorithm and the "mark-compact" algorithm. </p><p style="text-align: left;">Both algorithms include two phases: the marking phase and the cleaning or compaction phase. </p><p style="text-align: left;">在标记阶段,所有堆上的活跃对象都会被标记。每个页都会包含一个用来标记的位图,位图中的每一位对应页中的一字(译注:一个指针就是一字大小)。这个标记非常有必要,因为指针可能会在任何字对齐的地方出现。显然,这样的位图要占据一定的空间(32位系统上占据3.1%,64位系统上占据1.6%),但所有的内存管理机制都需要这样占用,因此这种做法并不过分。除此之外,另有2位来表示标记对象的状态。由于对象至少有2字长,因此这些位不会重叠。状态一共有三种:如果一个对象的状态为白,那么它尚未被垃圾回收器发现;如果一个对象的状态为灰,那么它已被垃圾回收器发现,但它的邻接对象仍未全部处理完毕;如果一个对象的状态为黑,则它不仅被垃圾回收器发现,而且其所有邻接对象也都处理完毕。</p><p style="text-align: left;">如果将堆中的对象看作由指针相互联系的有向图,标记算法的核心实际是深度优先搜索。在标记的初期,位图是空的,所有对象也都是白的。从根可达的对象会被染色为灰色,并被放入标记用的一个单独分配的双端队列。标记阶段的每次循环,GC会将一个对象从双端队列中取出,染色为黑,然后将它的邻接对象染色为灰,并把邻接对象放入双端队列。这一过程在双端队列为空且所有对象都变黑时结束。特别大的对象,如长数组,可能会在处理时分片,以防溢出双端队列。如果双端队列溢出了,则对象仍然会被染为灰色,但不会再被放入队列(这样他们的邻接对象就没有机会再染色了)。因此当双端队列为空时,GC仍然需要扫描一次,确保所有的灰对象都成为了黑对象。对于未被染黑的灰对象,GC会将其再次放入队列,再度处理。</p><p style="text-align: left;">以下是标记算法的伪码:</p><pre class="brush:php;toolbar:false">markingDeque = []
	overflow = false
	def markHeap():
	 for root in roots:
	 mark(root)
	 do:
	 if overflow:
	  overflow = false
	  refillMarkingDeque()
	 while !markingDeque.isEmpty():
	  obj = markingDeque.pop()
	  setMarkBits(obj, BLACK)
	  for neighbor in neighbors(obj):
	  mark(neighbor)
	 while overflow
	 
	def mark(obj):
	 if markBits(obj) == WHITE:
	 setMarkBits(obj, GREY)
	 if markingDeque.isFull():
	  overflow = true
	 else:
	  markingDeque.push(obj)
	def refillMarkingDeque():
	 for each obj on heap:
	 if markBits(obj) == GREY:
	  markingDeque.push(obj)
	  if markingDeque.isFull():
	  overflow = true
	  return

标记算法结束时,所有的活跃对象都被染为了黑色,而所有的死对象则仍是白的。这一结果正是清理和紧缩两个阶段所期望的。

标记算法执行完毕后,我们可以选择清理或是紧缩,这两个算法都可以收回内存,而且两者都作用于页级(注意,V8的内存页是1MB的连续内存块,与虚拟内存页不同)。

清理算法扫描连续存放的死对象,将其变为空闲空间,并将其添加到空闲内存链表中。每一页都包含数个空闲内存链表,其分别代表小内存区(

紧缩算法会尝试将对象从碎片页(包含大量小空闲内存的页)中迁移整合在一起,来释放内存。这些对象会被迁移到另外的页上,因此也可能会新分配一些页。而迁出后的碎片页就可以返还给操作系统了。迁移整合的过程非常复杂,因此我只提及一些细节而不全面讲解。大概过程是这样的。对目标碎片页中的每个活跃对象,在空闲内存链表中分配一块其它页的区域,将该对象复制至新页,并在碎片页中的该对象上写上转发地址。迁出过程中,对象中的旧地址会被记录下来,这样在迁出结束后V8会遍历它所记录的地址,将其更新为新的地址。由于标记过程中也记录了不同页之间的指针,此时也会更新这些指针的指向。注意,如果一个页非常“活跃”,比如其中有过多需要记录的指针,则地址记录会跳过它,等到下一轮垃圾回收再进行处理。

增量标记与惰性清理

你应该想到了,当一个堆很大而且有很多活跃对象时,标记-清除和标记-紧缩算法会执行的很慢。起初我研究V8时,垃圾回收所引发的500-1000毫秒的停顿并不少见。这种情况显然很难接受,即使是对于移动设备。

2012年年中,Google引入了两项改进来减少垃圾回收所引起的停顿,并且效果显著:增量标记和惰性清理。

Incremental marking allows marking of the heap to occur in several small pauses of 5-10 milliseconds (mobile devices). Incremental marking is enabled when the heap size reaches a certain threshold. After being enabled, whenever a certain amount of memory is allocated, the execution of the script will be paused and an incremental marking will be performed. Just like ordinary marking, incremental marking is also a depth-first search and uses the same white-gray-black mechanism to classify objects.

But the difference between incremental markers and ordinary markers is that the graph relationship of the object may change! What we need to pay special attention to is the new pointers pointing from the black object to the white object. Recall that a black object means that it has been completely scanned by the garbage collector and will not be scanned again. Therefore, if a pointer like "black → white" appears, we may miss the white object and treat it as a dead object by mistake. (Annotation: The remaining white objects after the marking process are considered dead objects.) So we had to enable the write barrier again. Now the write barrier not only records the "old → new" pointer, but also records the "black → white" pointer. Once such a pointer is found, the black object will be recolored into a gray object and put back into the deque. When the algorithm takes out the object, the pointers it contains are rescanned so that active white objects are not missed.

After the incremental marking is completed, lazy cleanup begins. All objects have been processed, so it is either dead or alive, and it is a foregone conclusion how much space on the heap can become free. At this time, we can not rush to release those spaces, and it is not a big deal to delay the cleaning process. Therefore, there is no need to clean all pages at once. The garbage collector will clean them one by one as needed until all pages are cleaned. At this time, the incremental mark is ready to go again.

Google has also recently added support for parallel cleaning. Since the script's execution thread will no longer touch dead objects, the page cleanup task can be performed in a separate thread with minimal synchronization work. The same support is being worked on for parallel marking, but is currently in an early experimental stage.

Summary

Garbage collection is really complicated. I've skipped a lot of details in the article and it still got very long. A colleague of mine said that he felt that working on the garbage collector was scarier than register allocation, and I said that it was indeed the case. In other words, I would rather leave these tedious details to the runtime than leave it to all application developers. Although garbage collection has some performance issues and occasional weirdness, it frees us from a lot of details so that we can focus on more important things.

I believe you have mastered the method after reading the case in this article. For more exciting information, please pay attention to other related articles on the php Chinese website!

Recommended reading:

How to use nodeJs crawler

How to use render and send in node.js

The above is the detailed content of How to use the garbage collector. For more information, please follow other related articles on the PHP Chinese website!

Statement
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Java vs JavaScript: A Detailed Comparison for DevelopersJava vs JavaScript: A Detailed Comparison for DevelopersMay 16, 2025 am 12:01 AM

JavaandJavaScriptaredistinctlanguages:Javaisusedforenterpriseandmobileapps,whileJavaScriptisforinteractivewebpages.1)Javaiscompiled,staticallytyped,andrunsonJVM.2)JavaScriptisinterpreted,dynamicallytyped,andrunsinbrowsersorNode.js.3)JavausesOOPwithcl

Javascript Data Types : Is there any difference between Browser and NodeJs?Javascript Data Types : Is there any difference between Browser and NodeJs?May 14, 2025 am 12:15 AM

JavaScript core data types are consistent in browsers and Node.js, but are handled differently from the extra types. 1) The global object is window in the browser and global in Node.js. 2) Node.js' unique Buffer object, used to process binary data. 3) There are also differences in performance and time processing, and the code needs to be adjusted according to the environment.

JavaScript Comments: A Guide to Using // and /* */JavaScript Comments: A Guide to Using // and /* */May 13, 2025 pm 03:49 PM

JavaScriptusestwotypesofcomments:single-line(//)andmulti-line(//).1)Use//forquicknotesorsingle-lineexplanations.2)Use//forlongerexplanationsorcommentingoutblocksofcode.Commentsshouldexplainthe'why',notthe'what',andbeplacedabovetherelevantcodeforclari

Python vs. JavaScript: A Comparative Analysis for DevelopersPython vs. JavaScript: A Comparative Analysis for DevelopersMay 09, 2025 am 12:22 AM

The main difference between Python and JavaScript is the type system and application scenarios. 1. Python uses dynamic types, suitable for scientific computing and data analysis. 2. JavaScript adopts weak types and is widely used in front-end and full-stack development. The two have their own advantages in asynchronous programming and performance optimization, and should be decided according to project requirements when choosing.

Python vs. JavaScript: Choosing the Right Tool for the JobPython vs. JavaScript: Choosing the Right Tool for the JobMay 08, 2025 am 12:10 AM

Whether to choose Python or JavaScript depends on the project type: 1) Choose Python for data science and automation tasks; 2) Choose JavaScript for front-end and full-stack development. Python is favored for its powerful library in data processing and automation, while JavaScript is indispensable for its advantages in web interaction and full-stack development.

Python and JavaScript: Understanding the Strengths of EachPython and JavaScript: Understanding the Strengths of EachMay 06, 2025 am 12:15 AM

Python and JavaScript each have their own advantages, and the choice depends on project needs and personal preferences. 1. Python is easy to learn, with concise syntax, suitable for data science and back-end development, but has a slow execution speed. 2. JavaScript is everywhere in front-end development and has strong asynchronous programming capabilities. Node.js makes it suitable for full-stack development, but the syntax may be complex and error-prone.

JavaScript's Core: Is It Built on C or C  ?JavaScript's Core: Is It Built on C or C ?May 05, 2025 am 12:07 AM

JavaScriptisnotbuiltonCorC ;it'saninterpretedlanguagethatrunsonenginesoftenwritteninC .1)JavaScriptwasdesignedasalightweight,interpretedlanguageforwebbrowsers.2)EnginesevolvedfromsimpleinterpreterstoJITcompilers,typicallyinC ,improvingperformance.

JavaScript Applications: From Front-End to Back-EndJavaScript Applications: From Front-End to Back-EndMay 04, 2025 am 12:12 AM

JavaScript can be used for front-end and back-end development. The front-end enhances the user experience through DOM operations, and the back-end handles server tasks through Node.js. 1. Front-end example: Change the content of the web page text. 2. Backend example: Create a Node.js server.

See all articles

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Atom editor mac version download

Atom editor mac version download

The most popular open source editor

Dreamweaver Mac version

Dreamweaver Mac version

Visual web development tools

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Safe Exam Browser

Safe Exam Browser

Safe Exam Browser is a secure browser environment for taking online exams securely. This software turns any computer into a secure workstation. It controls access to any utility and prevents students from using unauthorized resources.

SublimeText3 English version

SublimeText3 English version

Recommended: Win version, supports code prompts!