JavaScript內存管理機制以及四種常見的內存洩漏解析

2021-03-02 GitChat精品課

↑ 點擊上方藍字關注我們,和小夥伴一起聊技術!

幾個星期前,我們開始編寫深入研究JavaScript工作原理的系列文章。通過閱讀這些文章,你可以了解到JavaScript的構建塊及其交互原理,從而能夠編寫出更好的代碼(前排提示:文中所有標藍部分均可閱讀原文獲取詳情)。

本系列的第一篇文章簡單介紹了引擎、運行時間和堆棧的調用。第二篇文章研究了谷歌V8 JavaScript引擎的內部機制,並介紹了一些編寫JavaScript代碼的技巧。

而這第三篇文章將討論另一個很重要的主題——內存管理。隨著程式語言變得越來越成熟越來越複雜,開發人員很容易忽視這一問題。同時,本文還將提供一些處理JavaScript內存洩漏的技巧,既能確保SessionStack不會出現內存洩漏,也不會增加web應用程式的內存佔用。

概述

像C這樣的程式語言都會有低級別的內存管理原語,例如malloc()和free()。開發人員使用這些原語能夠顯式地對內存進行分配和釋放。

而JavaScript會在對象(對象、字符串等)創建時為它們分配內存,在對象不再使用時,「自動」釋放內存。這個過程我們稱之為垃圾收集。這種看似很「自動化」的資源釋放機制其實是混亂的根源,因為這給JavaScript(以及其他高級語言)開發人員帶來了一種錯覺,認為自己可以不用管理內存。這種想法是錯誤的。

即使是使用高級語言,開發人員也應該了解一些內存管理方面的知識(或者至少懂得一些基礎知識)。因為在自動內存管理(比如垃圾收集器的bug或實現限制等)出現問題的時候,開發人員必須能夠理解並正確地解決這些問題(或者找到一個合適的解決方案,以最低的代價來修改代碼)。

內存的生命周期

無論使用哪種程式語言,內存的生命周期都是一樣的:

這裡簡單介紹一下內存生命周期中的每一個階段:

分配內存——內存由作業系統分配,並允許程序使用它。在低級語言(例如C)中,開發人員必須顯式地執行這一操作。而在高級語言中,系統會自動為你分配內存。

使用內存——在這一步中,程序將使用先前分配的內存。在代碼中使用已分配過內存的變量時,就會發生內存讀寫操作。

釋放內存——釋放所有不再使用的內存,使之成為自由內存,並可以被重利用。與分配內存操作一樣,這一操作在低級語言中也是需要顯式地執行。

要快速了解調用棧和內存堆的相關概念,你可以閱讀本系列的第一篇文章。

內存是什麼?

在介紹JavaScript中的內存之前,我們先來簡單討論一下什麼是內存,以及它是如何工作的。

在硬體層面上,計算機存儲器由大量的觸發器組成。每個觸發器包含了一些電晶體,並且能夠存儲一個比特(bit,又稱「位」)。單個觸發器由唯一的標識符來尋址,這樣我們就能夠讀取和覆蓋它們。因此,從概念上講,可以把整個計算機內存看作是可以讀寫的一個巨大數組。

因為我們並不擅長用比特來思考和計算,所以要把它們組織成更大的群體,這樣才可以用來表示數字。8個比特稱為1個字節(byte)。除了字節之外,還有字(word,有時是16位,有時是32位)。

很多東西都存儲在內存中:

程序使用的所有變量和其他數據。

程序的代碼,包括作業系統的代碼。

編譯器和作業系統會為你處理大部分的內存管理工作,但你還是需要了解一下底層到底發生了什麼。

編譯代碼時,編譯器會檢查原始數據類型並提前計算所需的內存,然後將所需的數量分配給調用堆棧空間中的程序。為這些變量分配的空間稱為棧空間,因為當函數被調用時,它們的內存就會被添加到現有內存中。當調用終止時,它們將會在LIFO命令(後進先出)中被移除。例如,看一下這個聲明:

int n; // 4個字節

int x[4]; // 4個元素的數組,每個元素4個字節

double m; // 8個字節

編譯器能夠立即知道所需的內存:4 + 4×4 + 8 = 28位元組

這段代碼展示了整型和雙精度浮點型變量所佔內存的大小。但是大約20年前,整型變量通常佔2個字節,而雙精度浮點型變量佔4個字節。你的代碼不應該依賴於當前基本數據類型的大小。

編譯器會插入與作業系統交互的代碼,並同時在棧上申請要存儲的變量所需的字節數。

在上面這個例子中,編譯器知道每個變量準確的內存地址。事實上,當我們寫入變量n時,它就會被翻譯成類似「內存地址4127963」這樣的內部信息。

注意,如果嘗試訪問x[4],那就會訪問到與m相關的數據。這是因為在數組中訪問一個不存在的元素(它比數組中最後一個實際分配的元素x[3]還要大4個字節),最終可能會讀取(或重寫) 到m的位,這肯定會對程序的其餘部分產生不可預知的結果。

當一個函數調用其他函數時,每個函數都會得到自己的棧塊。它保存了所有的局部變量,同時還有一個程序計數器,用於記錄程序執行的位置。當函數執行完成時,它的內存塊就可用於其他地方了。

動態分配

不幸的是,如果在編譯時不知道變量需要多少內存,那情況就有點複雜了。假設要進行如下的操作:

int n = readInput(); // reads input from the user

...

// create an array with "n" elements

在編譯時,編譯器不知道數組需要使用多少內存,因為這是由用戶提供的值決定的。

因此,不能為棧上的變量分配空間。相反,程序需要在運行時明確地向作業系統請求適當大小的空間。這個內存是在堆空間上分配。靜態內存和動態內存分配的區別,請見下面這個表格:

要完全理解動態內存分配的原理,我們需要多研究研究指針,這可能有點偏離本文的主題了。

在JavaScript中分配內存

現在將解釋第一步:如何在JavaScript中分配內存。

JavaScript把開發人員從內存分配的責任中解救了出來:JavaScript能自己完成這項工作,同時進行賦值。

var n = 374; // allocates memory for a number

var s = 'sessionstack'; // allocates memory for a string 

var o = {

  a: 1,

  b: null

}; // allocates memory for an object and its contained values

var a = [1, null, 'str'];  // (like object) allocates memory for the

                           // array and its contained values

function f(a) {

  return a + 3;

} // allocates a function (which is a callable object)

// function expressions also allocate an object

someElement.addEventListener('click', function() {

  someElement.style.backgroundColor = 'blue';

}, false);

某些函數調用也會導致對象的內存分配:

var d = new Date(); // allocates a Date object

var e = document.createElement('div'); // allocates a DOM element

分配新的值或對象:

var s1 = 'sessionstack';

var s2 = s1.substr(0, 3); // s2 is a new string

// Since strings are immutable, 

// JavaScript may decide to not allocate memory, 

// but just store the [0, 3] range.

var a1 = ['str1', 'str2'];

var a2 = ['str3', 'str4'];

var a3 = a1.concat(a2); 

// new array with 4 elements being

// the concatenation of a1 and a2 elements

在JavaScript中使用內存

在JavaScript中使用分配的內存就意味著對內存進行讀寫,而這可以通過讀寫一個變量的值或者對象的屬性,或者將參數傳遞給函數來實現。

當內存不再需要時進行釋放

大多數的內存管理問題都出現在這個階段。

最困難的工作在於計算出何時不再需要已分配的內存,這通常要求開發人員來決定在程序中哪些地方不再需要內存,並將其釋放。

高級語言中嵌入了一種稱為垃圾收集器的軟體,它的工作是跟蹤內存的分配和使用,以便在任何情況下找到一塊不再需要的已分配內存,並自動將其釋放。

不幸的是,這個過程只是進行粗略估計,因為很難知道某塊內存是否真的需要 (不能通過算法來解決)。

垃圾收集器大多數的工作是收集無法訪問的內存,例如,所有指向這塊內存的變量都超出了作用域。但是,這些收集到的內存空間並不完整。因為在任何時候都可能存在這麼一塊內存:有一個變量指向了它,但它卻永遠不會被訪問到。

垃圾收集

由於很難判斷某塊內存是否真的有用,因此,垃圾收集器想了一個辦法來解決這個問題。本節將主要介紹垃圾收集的算法及其局限性。

內存引用

垃圾收集算法主要依賴的是引用。

在內存管理中,如果一個對象可以訪問另一個對象,則稱它在引用另一個對象(可以是隱式的或顯式的)。例如,一個JavaScript對象引用它的原型(隱式引用)和它的屬性值(顯式引用)。

在這種情況下,「對象」這個概念就擴展到了比常規JavaScript對象更廣泛的領域,並且還包含了函數作用域(或全局範圍)。

引用計數垃圾收集算法

這是最簡單的垃圾收集算法。如果沒有指針指向一個對象,那這個對象就被認為是「可收集的垃圾」。

看下面的代碼:

var o1 = {

  o2: {

    x: 1

  }

};

// 2 objects are created. 

// 'o2' is referenced by 'o1' object as one of its properties.

// None can be garbage-collected

var o3 = o1; // the 'o3' variable is the second thing that 

            // has a reference to the object pointed by 'o1'. 

o1 = 1;      // now, the object that was originally in 'o1' has a         

            // single reference, embodied by the 'o3' variable

var o4 = o3.o2; // reference to 'o2' property of the object.

                // This object has now 2 references: one as

                // a property. 

                // The other as the 'o4' variable

o3 = '374'; // The object that was originally in 'o1' has now zero

            // references to it. 

            // It can be garbage-collected.

            // However, what was its 'o2' property is still

            // referenced by the 'o4' variable, so it cannot be

            // freed.

o4 = null; // what was the 'o2' property of the object originally in

           // 'o1' has zero references to it. 

           // It can be garbage collected.

循環會產生問題

當涉及到循環時,會有一個限制。在下面的示例中,創建了兩個對象,兩個對象互相調用,從而創建了一個循環。在函數調用之後將超出作用域,因此它們實際上是無用的,可以被釋放。然而,引用計數算法認為,由於每個對象至少被引用一次,所以它們都不能被垃圾收集。

function f() {

  var o1 = {};

  var o2 = {};

  o1.p = o2; // o1 references o2

  o2.p = o1; // o2 references o1. This creates a cycle.

}

f();

標記-清除(Mark-and-sweep)算法

該算法能夠判斷出某個對象是否可以訪問,從而知道該對象是否有用。

該算法由以下步驟組成:

垃圾收集器構建一個「根」列表,用於保存引用的全局變量。在JavaScript中,「window」對象是一個可作為根節點的全局變量。

所有根節點都會被檢查並標記為活動的(也就是說不是垃圾)。子節點都是遞歸檢查的,所有可以從根節點中得到的都不被認為是垃圾。

所有未標記為活動的內存碎片都被視為垃圾。收集器現在可以釋放這些內存並將其還給作業系統。

這個算法比上一個算法要好,因為「一個對象沒有被引用」就意味著這個對象無法訪問。

截止到2012年,所有的現代瀏覽器都有一個「標記-清除」垃圾收集器。在過去的幾年裡,JavaScript在垃圾收集(生成、增量、並發、並行的垃圾收集)領域所做的所有改進都是對該算法實現的改進(標記和清除),而不是對垃圾收集算法本身的改進。

在這篇文章中,你可以更詳細地閱讀到有關跟蹤垃圾收集的詳細信息,同時還包括了標記-清除算法及其優化。

循環不再是問題

在上面的第一個例子中,函數調用返回後,那兩個對象就不再被全局對象可訪問的東西所引用。因此,垃圾收集器會認為它們不可訪問。

儘管對象之間存在引用,但它們對於根節點來說是不可達的。

垃圾收集器的反直觀行為

儘管垃圾收集器很方便,但它們有一套自己的折衷方案,其中之一就是非決定論,換句話說,GC是不可預測的,你無法真正判斷何時進行垃圾收集。這意味著在某些情況下,程序會使用更多的內存,這實際上是必需的。在對速度特別敏感的應用程式中,可能會很明顯的感受到短時間的停頓。如果沒有分配內存,則大多數GC將處於空閒狀態。看看以下場景:

在此場景中,大多數GC將不再繼續收集。換句話說,即使是不可用的引用,收集器也不會奪走這些引用。雖然這些並不是嚴重的內存洩漏,但仍然會出現高於平時內存使用的情況。

內存洩漏是什麼?

從本質上說,內存洩漏可以定義為:不再被應用程式所需要的內存,出於某種原因,它不會返回到作業系統或空閒內存池中。

程式語言支持不同的內存管理方法。然而,某一塊內存是否被使用實際上無法判斷。換句話說,只有開發人員才知道這塊內存是否可以還給作業系統。

某些程式語言為開發人員提供了幫助,另一些則期望開發人員能清楚地了解內存何時不再被使用。維基百科上有一些有關人工和自動內存管理的很不錯的文章。

四種常見的內存洩漏

1.全局變量

JavaScript以一種非常有趣的方式來處理未聲明的變量: 對於未聲明的變量,會在全局範圍中創建一個新的變量來對其進行引用。對瀏覽器來說,全局對象是window。例如:

function foo(arg) {

    bar = "some text";

}

等價於:

function foo(arg) {

    window.bar = "some text";

}

如果bar在foo函數的作用域內對一個變量進行引用,卻忘記使用var來聲明它,那麼將創建一個意想不到的全局變量。

在這個例子中,遺漏一個簡單的字符串不會造成太大的危害,但這肯定會很糟。

創建一個意料之外的全局變量的另一種方法是使用this:

function foo() {

    this.var1 = "potential accidental global";

}

// Foo called on its own, this points to the global object (window)

// rather than being undefined.

foo();

要防止這些錯誤發生,可以在JavaScript文件的開頭添加』use strict』。這就啟用了更嚴格的JavaScript解析模式,以防止意外的全局變量。你可以在這裡了解更多到有關這種JavaScript執行的模式。

儘管我們討論的是未知的全局變量,但仍然有很多代碼充斥著顯式的全局變量。根據定義,這些是不可收集的(除非被指定為空或重新分配)。用於臨時存儲和處理大量信息的全局變量特別令人擔憂。如果你必須使用一個全局變量來存儲大量數據,那麼請確保將其指定為null,或者在完成後將其重新賦值。

2. 被遺忘的定時器和回調

在JavaScript中,setInterval的使用很常見。

大多數提供了觀察器和採用回調工具的庫,都會在自身實例變得不可訪問時,自動將指向回調的引用置為不可訪問。然而,對於setInterval來說,這樣的代碼很常見:

var serverData = loadData();

setInterval(function() {

    var renderer = document.getElementById('renderer');

    if(renderer) {

        renderer.innerHTML = JSON.stringify(serverData);

    }

}, 5000); //This will be executed every ~5 seconds.

這個例子描述了該定時器在運行時具體發生了什麼:定時器引用了那些不再需要的節點或數據。

renderer表示的對象可能會在未來的某個時間點被刪除,從而導致內部處理程序中的一整塊代碼都變得不再需要。但是,由於定時器仍然是活動的,所以,處理程序不能被收集,並且其依賴項也無法被收集。這意味著,存儲著大量數據的serverData也不能被收集。

對觀察器來說,當變量不再需要的時候,需要顯示地刪除它們(或者是無法訪問的關聯對象)。

過去的某些瀏覽器(IE 6)不能很好地管理循環引用,但這一點卻尤為重要。現在,一旦被監視對象變得不可訪問,即使監聽器沒有被顯式刪除,大多數瀏覽器也能對其進行收集。然而,我們還是應該在對象被處理之前顯式地刪除這些觀察者。例如:

var element = document.getElementById('launch-button');

var counter = 0;

function onClick(event) {

   counter++;

   element.innerHtml = 'text ' + counter;

}

element.addEventListener('click', onClick);

// Do stuff

element.removeEventListener('click', onClick);

element.parentNode.removeChild(element);

// Now when element goes out of scope,

// both element and onClick will be collected even in old browsers // that don't handle cycles well.

現代瀏覽器(包括Internet Explorer和Microsoft Edge)使用了先進的垃圾收集算法來檢測這些循環並能夠正確處理它們。換句話說,在將節點置為不可訪問之前,無需嚴格調用removeEventListener。

一些框架或庫,比如JQuery,會在處置節點之前自動刪除監聽器(在使用它們特定的API的時候)。這是由庫內部的機制實現的,能夠確保不發生內存洩漏,即使在有問題的瀏覽器下運行也能這樣,比如……IE 6。

3.閉包

JavaScript開發中有一個關鍵點,即閉包:一個能夠訪問外部(封閉)函數變量的內部函數。由於JavaScript運行時的實現細節存在問題,下面這個代碼會產生內存洩漏:

var theThing = null;

var replaceThing = function () {

  var originalThing = theThing;

  var unused = function () {

    if (originalThing) // a reference to 'originalThing'

      console.log("hi");

  };

  theThing = {

    longStr: new Array(1000000).join('*'),

    someMethod: function () {

      console.log("message");

    }

  };

};

setInterval(replaceThing, 1000);

這段代碼做了一件事:每次調用replaceThing的時候,theThing都會得到一個包含一個大數組和一個新閉包(someMethod)的新對象。同時,變量unused指向一個引用了originalThing的閉包。是不是很混亂,嘿嘿?重要的是,一旦具有相同父作用域的多個閉包的作用域被創建,則這個作用域就可以被共享。

在這種情況下,為閉包someMethod而創建的作用域可以被unused共享的。unused內部存在一個對originalThing的引用。即使unused從未使用過,someMethod也可以在replaceThing的作用域之外(例如在全局範圍內)通過theThing來被調用。由於someMethod共享了unused閉包的作用域,那麼unused引用包含的originalThing會迫使它保持活動狀態(兩個閉包之間的整個共享作用域)。這阻止了它被收集。

當這段代碼重複運行時,可以觀察到內存使用在穩定增長,當GC運行後,內存使用也不會變小。從本質上說,在運行過程中創建了一個閉包鍊表(它的根是以變量theThing的形式存在),並且每個閉包的作用域都間接引用了了一個大數組,這造成了相當大的內存洩漏。

這個問題是Meteor小組發現的,他們寫了一篇不錯的文章詳細地描述了這個問題。

4. 脫離DOM的引用

有時,將DOM節點存儲在數據結構中可能會很有用。假設你希望快速地更新表中的幾行內容,那麼你可以在一個字典或數組中保存每個DOM行的引用。這樣,同一個DOM元素就存在兩個引用:一個在DOM樹中,另一個則在字典中。如果在將來的某個時候你決定刪除這些行,那麼你需要將這兩個引用都設置為不可訪問。

var elements = {

    button: document.getElementById('button'),

    image: document.getElementById('image')

};

function doStuff() {

    elements.image.src = 'http://example.com/image_name.png';

}

function removeImage() {

    // The image is a direct child of the body element.

    document.body.removeChild(document.getElementById('image'));

    // At this point, we still have a reference to #button in the

    //global elements object. In other words, the button element is

    //still in memory and cannot be collected by the GC.

}

當涉及到DOM樹的內部或葉節點時,還需要額外注意一個問題。假設你在JavaScript代碼中有一個指向某個表(<td>標記)的特定單元格的引用。有一天,你決定從DOM中刪除這個表,但要保留對該單元格的引用。人們可能會認為GC會收集所有的東西,除了單元格。但事實上,這種情況並不會發生。單元格是該表的子節點,而子節點則會引用父節點。也就是說,JavaScript代碼中引用整個表的單元格會使得整個表留在內存中。在保存對DOM元素的引用時,要仔細考慮這個問題。

在SessionStack,我們編寫代碼的時候一直遵循著這些最佳實踐,並對處理內存分配十分謹慎,因為:

一旦將SessionStack集成到你web應用程式中,它就會開始記錄所有的內容,包括:所有DOM的更改、用戶交互、JavaScript異常、堆棧跟蹤、失敗的網絡請求、調試消息等等。通過使用SessionStack,你可以將web應用程式中的問題作為視頻進行回放,並查看發生在用戶身上的所有內容。所有這些都必須在對web應用程式性能沒有影響的情況下進行。

由於用戶可以重新加載頁面或瀏覽web應用,所以你必須正確處理所有的監視器、攔截器、變量分配等等,這樣,才不會出現任何形式的內存洩漏,也不會增加所集成的web應用的內存佔用。

我們有一個免費的試用體驗,你可以試一試。

參考資源

http://www-bcf.usc.edu/~dkempe/CS104/08-29.pdf

https://blog.meteor.com/an-interesting-kind-of-javascript-memory-leak-8b47d2e7f156

http://www.nodesimplified.com/2017/08/javascript-memory-management-and.html

原文:How JavaScript works: memory management + how to handle 4 common memory leaks 

作者:Alexander Zlatkov 

譯者:雁驚寒

相關焦點

  • Android 內存洩漏探討
    我會從 java 內存洩漏的基礎知識開始,並通過具體例子來說明 Android 引起內存洩漏的各種原因,以及如何利用工具來分析應用內存洩漏,最後再做總結。篇幅有些長,大家可以分幾節來看!另外一種常用的內存管理技術是使用計數器,例如COM模型採用計數器方式管理構件,它與有向圖相比,精度行低(很難處理循環引用的問題),但執行效率很高。什麼是Java中的內存洩露在Java中,內存洩漏就是存在一些被分配的對象,這些對象有下面兩個特點,首先,這些對象是可達的,即在有向圖中,存在通路可以與其相連;其次,這些對象是無用的,即程序以後不會再使用這些對象。
  • 淺析java內存管理機制
    不同的程式語言有不同的內存管理機制,本文在對比C++和Java語言內存管理機制的不同的基礎上,淺析java中的內存分配和內存回收機制,包括java對象初始化及其內存分配,內存回收方法及其注意事項等……但是C++這種直接操作內存的方式存在很大內存洩露風險,而且人為管理內存複雜且困難。
  • 【文末送書】JavaScript內存管理介紹
    大多數時候,我們在不了解有關內存管理的知識下也只開發,因為 JS 引擎會為我們處理這個問題。不過,有時候我們會遇到內存洩漏之類的問題,這個只有知道內存分配是怎樣工作的,我們才能解決這些問題。在本文中,主要介紹內存分配和垃圾回收的工作原理以及如何避免一些常見的內存洩漏問題。
  • Android 輕鬆解決內存洩漏
    的強引用就會導致內存洩漏。,造成內存洩漏。使用系統服務引發的內存洩漏為了方便我們使用一些常見的系統服務,Activity 做了一些封裝。WebView 引發的內存洩漏WebView 解析網頁時會申請Native堆內存用於保存頁面元素,當頁面較複雜時會有很大的內存佔用。如果頁面包含圖片,內存佔用會更嚴重。
  • iOS 內存洩漏場景與解決方案
    (程式設計師自己管理的空間)由於某些原因未能釋放或無法釋放,造成系統內存的浪費,導致程序運行速度變慢甚至系統崩潰。雖然現在已經普及了 ARC 模式,但它僅對 OC 對象進行自動內存管理。,需要用 free 去釋放,否則會出現內存洩漏。
  • JavaScript 中 4 種常見的內存洩露陷阱
    在這篇文章中我們將要探索客戶端 JavaScript 代碼中常見的一些內存洩漏的情況,並且學習如何使用 Chrome 的開發工具來發現他們。讀一讀吧!介紹內存洩露是每個開發者最終都不得不面對的問題。即便使用自動內存管理的語言,你還是會碰到一些內存洩漏的情況。內存洩露會導致一系列問題,比如:運行緩慢,崩潰,高延遲,甚至一些與其他應用相關的問題。
  • 【221期】面試官:談談內存洩漏和內存溢出的聯繫與區別
    2、沒有釋放動態分配的存儲空間而造成內存洩漏,是動態存儲變量的主要問題。3、常用內存管理函數如下:malloc,free,calloc,recalloc等,來完成動態存儲變量存儲空間的分配和釋放。4、常見的內存管理錯誤如下子函數分配的內存空間在主函數出現異常中斷時,或主函數對子函數返回的信息使用結束時,沒有對分配的內存進行釋放;程序實現過程中分配的臨時內存在程序結束時,沒有釋放臨時內存;內存錯誤一般是不可在現的,開發人員不易在程序調式和測試階段發現,即使花費了很多的時間和精力,也無法消除。
  • 檢查是否內存洩漏的方法
    目錄前言一個簡單的內存洩漏檢查內存洩露的方法方法內存洩漏時,內存的消耗沒有內存洩露時的內存消耗前言這兩天抽空把之前買的這門課給看了看,課程很好,有興趣的也可以看一看:具體的課程總結過幾天寫一寫,這篇推送就說一下從這課上學到的一個技巧吧:一個檢查內存洩露的方法。
  • 理解閉包與內存洩漏
    三、內存洩漏內存洩漏常常與閉包緊緊聯繫在一起,很容易讓人誤以為閉包就會導致內存洩漏。其實閉包只是讓內存常駐,而濫用閉包才會導致內存洩漏。當然並不是說內存dump文件的大小不斷增大就存在內存洩漏,如果應用的訪問量確實在一直增大,那麼內存曲線只增不減也屬於正常情況,我們只能根據具體情況判斷是否存在內存洩漏的可能。
  • 常見的 JavaScript 內存洩露
    最後希望各位能從文章中有所收穫>🎉 enjoy reading, enjoy life 🐳✏️最新內容請以github上的為準❗️https://github.com/zhansingsong/js-leakage-patterns什麼是內存洩露內存洩漏指由於疏忽或錯誤造成程序未能釋放已經不再使用的內存。
  • iOS內存洩漏的檢測及定位工具-Instruments
    ,後來經過和開發小哥哥溝通,分析得知可能是內存洩漏,於是利用Instruments工具進行檢測發現果然存在內存洩漏,並成功定位到內存洩漏的代碼所在,問題完美解決。上述內存洩漏問題的定位和完美解決離不開一個工具Instruments,測試遇到的內存問題中最常見就是內存洩漏,在此小編介紹一個內存洩漏檢測工具- Instruments之Leaks,使用它可以檢測程序是否存在內存洩露,並定位到內存洩露的代碼。
  • Android 性能優化之內存洩漏,使用MAT&LeakCanary解決問題
    ,內存這個小妮子比較調皮,每個月總有那麼幾次洩漏或者溢出(OOM),這篇文章所講的是內存溢出,這裡要注意,內存溢出和內存洩漏是兩個概念,這點大家要清楚,當然,內存洩漏過多會導致內存洩漏,至於什麼是內存洩漏呢,大家都知道我們的內存回收機制是GC,所以用一句話來概括:GC回收機制所無法回收的垃圾對象。
  • C語言中的指針和內存洩漏
    指針和內存洩漏對某些開發人員來說似乎令人畏懼,但是一旦您了解了指針及其關聯內存操作的基礎,它們就是您在 C 語言中擁有的最強大工具。本文將與您分享開發人員在開始使用指針來編程前應該知道的秘密。內存洩漏內存洩漏可能真正令人討厭。下面的列表描述了一些導致內存洩漏的場景。●重新賦值我將使用一個示例來說明重新賦值問題。char *memoryArea = malloc(10);char *newArea = malloc(10);這向如下面的圖 4所示的內存位置賦值。
  • 《Linux鐵三角之內存管理》在線視頻課程
    主要目的:理解硬體訪問內存的原理,MMU和頁表;澄清Linux內核ZONE,buddy,slab管理;澄清用戶空間malloc與內核關係,Lazy分配機制;澄清進程的內存消耗的vss,rss,pss,uss概念;澄清內存耗盡的OOM行為;澄清文件背景頁面與匿名頁,page cache與swap;澄清內存的回收、dirty page的寫回,
  • 用valgrind定位內存洩漏
    緣起年前,寫了使用mtrace定位內存洩漏,在留言中,有讀者提到了希望介紹valgrind,那好,今天就介紹使用valgrind定位內存洩漏。大約2-3年前,楊同學讓我幫做模擬面試,他求職的是C++崗位,我問了這樣一個問題:在你的項目中,你是如何定位內存洩漏的呢?
  • 理解NodeJS 的內存管理機制
    v8 的內存限制眾所周知,Node 是基於 v8 引擎來構建的,所以在 Node 中使用的對象基本都是通過 v8 引擎來統一進行內存分配和管理。然而 v8 引擎 本身對內存的使用限制了大小,在64位系統下只能用 1.4GB 的系統內存。
  • C語言內存洩漏,看完這篇就懂了!
    一方面,內存洩漏問題屬於比較淺顯的錯誤,此類問題遺漏到現網,影響不好;另一方面,由於內存洩漏問題很可能導致單板運行固定時間以後就復位,只能通過批量升級才能解決,實際影響不佳。本文通過介紹內存洩漏問題原理及檢視方法,希望後續能夠從編碼檢視環節就杜絕此類問題發生。
  • Linux如何調試內存洩漏
    內存洩漏是指由於疏忽或錯誤造成程序未能釋放已經不再使用的內存
  • Netty堆外內存洩漏排查,這一篇全講清楚了
    上篇文章介紹了Netty內存模型原理,由於Netty使用不當會導致堆外內存洩漏,網上關於這方面的資料比較少,所以寫下這篇文章,基於Netty4.1.43.Final,專門介紹排查Netty堆外內存相關的知識點,診斷工具,以及排查思路現象堆外內存洩漏的現象主要是,進程佔用的內存較高(Linux
  • C/C++程序內存問題分析
    本次,FourExperts小組整理了一些內存問題,希望通過對這些問題的分析,大家能夠了解到程序內存問題的類型以及一些基礎知識。程序內存洩漏問題是十分常見的一類程序問題,有很多原因都會導致程序表現出內存洩漏的現象。從造成洩漏的根本原因上劃分,我們可以簡單地將問題分為兩類:程序設計不合理:這類問題的表現為程序中丟失了申請內存的引用,導致這些內存再也無法引用,從而引起洩漏。這類原因也是非常基本的一類原因。