diff options
Diffstat (limited to 'files/vi/web/javascript/memory_management/index.html')
-rw-r--r-- | files/vi/web/javascript/memory_management/index.html | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/files/vi/web/javascript/memory_management/index.html b/files/vi/web/javascript/memory_management/index.html new file mode 100644 index 0000000000..3c3da3771d --- /dev/null +++ b/files/vi/web/javascript/memory_management/index.html @@ -0,0 +1,183 @@ +--- +title: Memory Management +slug: Web/JavaScript/Memory_Management +translation_of: Web/JavaScript/Memory_Management +--- +<div>{{JsSidebar("Advanced")}}</div> + +<h2 id="Giới_thiệu">Giới thiệu</h2> + +<p>Ngôn ngữ bậc thấp như C, có các nguyên hàm quản lý bộ nhớ theo cách thủ công như là<a href="https://pubs.opengroup.org/onlinepubs/009695399/functions/malloc.html">malloc()</a> và <a href="https://en.wikipedia.org/wiki/C_dynamic_memory_allocation#Overview_of_functions">free()</a>. Trái lại, JavaScript tự động cấp phát bộ nhớ khi object được khởi tạo và giải phóng khi object đó không còn được dùng nữa (<em>bộ thu thập rác</em>). Sự tự động này có thể gây ra bối rối: khiến cho nhà phát triển tưởng rằng họ không cần lo về vấn đề quản lý bộ nhớ.</p> + +<h2 id="Vòng_đời_bộ_nhớ">Vòng đời bộ nhớ</h2> + +<p>Với mọi ngôn ngữ lập trình, vòng đời bộ nhớ khá giống nhau:</p> + +<ol> + <li>Cấp phát bộ nhớ mà bạn cần</li> + <li>Sử dụng bộ nhớ đã cấp phát (đọc, ghi)</li> + <li>Giải phóng bộ nhớ đã cấp phát khi chúng không còn được sử dụng</li> +</ol> + +<p>Phần thứ hai tường minh trên mọi ngôn ngữ. Phần đầu và cuối tường minh trên họ ngôn ngữ bậc thấp nhưng ngầm trên ngôn ngữ bậc cao như JavaScript.</p> + +<h3 id="Cấp_phát_trong_JavaScript">Cấp phát trong JavaScript</h3> + +<h4 id="Khởi_tạo_giá_trị">Khởi tạo giá trị</h4> + +<p>Để không khiến lập trình viên phải bận tâm với việc cấp phát, JavaScript thực hiện điều đó khi khởi tạo giá trị.</p> + +<pre class="brush: js">var n = 123; // cấp phát bộ nhớ cho một số +var s = 'azerty'; // cấp phát bộ nhớ cho một xâu ký tự + +var o = { + a: 1, + b: null +}; // cấp phát bộ nhớ cho object và giá trị bên trong + +// (giống như object) cấp phát bộ nhớ cho mảng +// và giá trị bên trong +var a = [1, null, 'abra']; + +function f(a) { + return a + 2; +} // cấp phát cho hàm (là object khả gọi) + +// biểu thức function cũng được cấp phát như object +someElement.addEventListener('click', function() { + someElement.style.backgroundColor = 'blue'; +}, false); +</pre> + +<h4 id="Cấp_phát_thông_qua_lời_gọi_hàm">Cấp phát thông qua lời gọi hàm</h4> + +<p>Kết quả của vài lời gọi hàm là việc cấp phát cho object.</p> + +<pre class="brush: js">var d = new Date(); // cấp phát cho Date object + +var e = document.createElement('div'); // cấp phát cho phần tử DOM</pre> + +<p>Một số phương thức cấp phát giá trị hoặc object mới:</p> + +<pre class="brush: js">var s = 'azerty'; +var s2 = s.substr(0, 3); // s2 is a new string +// Vì xâu ký tự là giá trị không thể biến đổi +// JavaScript có thể sẽ không cấp phát thêm bộ nhớ +// mà chỉ lưu trữ đoạn [0, 3]. + +var a = ['ouais ouais', 'nan nan']; +var a2 = ['generation', 'nan nan']; +var a3 = a.concat(a2); +// mảng mới với 4 phần tử +// là kết quả của phép nối các phần tử của a và a2 +</pre> + +<h3 id="Sử_dụng_giá_trị">Sử dụng giá trị</h3> + +<p>Sử dụng giá trị về cơ bản nghĩa là đọc và ghi trên bộ nhớ đã cấp phát. Việc này có thể được thực hiện thông qua đọc hoặc ghi giá trị của một biến hoặc một thuộc tính của object hay thậm chí là truyền tham số vào một hàm.</p> + +<h3 id="Giải_phóng_khi_bộ_nhớ_không_còn_cần_tới">Giải phóng khi bộ nhớ không còn cần tới</h3> + +<p>Hầu hết các vấn đề về quản lý bộ nhớ đều xảy ra ở bước này. Nhiệm vụ khó nhất lúc này là tìm "bộ nhớ đã cấp phát không còn cần tới nữa". Thường lúc này nhà phát triển sẽ phải xác định phần bộ nhớ nào trong chương trình không còn cần tới nữa và giải phóng chúng.</p> + +<p>Ngôn ngữ bậc cao nhúng phần mềm có tên là "bộ thu thập rác" có nhiệm vụ dò theo việc cấp phát và sử dụng bộ nhớ để tìm ra "bộ nhớ đã cấp phát không còn cần tới nữa", nó sẽ tự động giải phóng phần bộ nhớ đó. Công cuộc này chỉ gần đúng bởi vì vấn đề chung để biết được liệu phần bộ nhớ đó có cần tới hay không là <a class="external" href="http://en.wikipedia.org/wiki/Decidability_%28logic%29">không thể quyết định</a> (không thể giải quyết bằng thuật toán).</p> + +<h2 id="Bộ_thu_thập_rác">Bộ thu thập rác</h2> + +<p>Như đã nhấn mạnh ở trên, vấn đề chung để tự động tìm ra phần bộ nhớ "không còn cần tới nữa" là không thể quyết định được. Vì vậy, bộ thu thập rác sử dụng tập hạn chế để giải quyết vấn đề chung đó. Phần này sẽ giải thích khái niệm cần thiết để hiểu được các thuật toán thu thập rác và hạn chế của chúng.</p> + +<h3 id="Tham_chiếu">Tham chiếu</h3> + +<p>Thuật toán thu thập rác chủ yếu phụ thuộc vào khái niệm <em>tham chiếu</em>. Trong ngữ cảnh quản lý bộ nhớ, object này được gọi là tham chiếu tới object khác nếu cái trước truy xuất tới cái sau (cả ngầm lẫn tường minh). Chẳng hạn, một JavaScript object tham chiếu tới <a href="/en-US/docs/Web/JavaScript/Guide/Inheritance_and_the_prototype_chain">prototype</a> (tham chiếu ngầm) và tới giá trị thuộc tính của nó (tham chiếu tường minh).</p> + +<p>Trong ngữ cảnh này, khái niệm "object" được hiểu rộng ra hơn với định nghĩa object của JavaScript và còn bao gồm phạm vi hàm (hoặc phạm vi lexical toàn cục).</p> + +<h3 id="Đếm_tham_chiếu">Đếm tham chiếu</h3> + +<p>Đây là thuật toán thu thập rác thô sơ nhất. Thuật toán rút gọn định nghĩa từ "một object không cần tới nữa" thành "một object không có tham chiếu nào tới nó". Một object được coi như là khả thu nếu không có tham chiếu nào trỏ tới nó.</p> + +<h4 id="Ví_dụ">Ví dụ</h4> + +<pre class="brush: js">var o = { + a: { + b: 2 + } +}; +// 2 object được khởi tạo. object này tham chiếu tới object kia như là thuộc tính của nó. +// The other is referenced by virtue of being assigned to the 'o' variable. +// Obviously, none can be garbage-collected + + +var o2 = o; // the 'o2' variable is the second thing that + // has a reference to the object +o = 1; // now, the object that was originally in 'o' has a unique reference + // embodied by the 'o2' variable + +var oa = o2.a; // reference to 'a' property of the object. + // This object now has 2 references: one as a property, + // the other as the 'oa' variable + +o2 = 'yo'; // The object that was originally in 'o' has now zero + // references to it. It can be garbage-collected. + // However its 'a' property is still referenced by + // the 'oa' variable, so it cannot be freed + +oa = null; // The 'a' property of the object originally in o + // has zero references to it. It can be garbage collected. +</pre> + +<h4 id="Hạn_chế_vòng">Hạn chế: vòng</h4> + +<p>Phát sinh hạn chế khi xét tới vòng. Trong ví dụ sau, hai object được khởi tạo và tham chiếu tới nhau, tạo ra vòng. Chúng sẽ không còn được dùng tới nữa sau lời gọi hàm, nên chúng sẽ trở thành vô dụng và khả thu. Tuy nhiên, thuật toán đếm tham chiếu vẫn xét là hai object tham chiếu lần lượt tới nhau, nên cả hai bất khả thu.</p> + +<pre class="brush: js">function f() { + var o = {}; + var o2 = {}; + o.a = o2; // o tham chiếu tới o2 + o2.a = o; // o2 tham chiếu tới o + + return 'azerty'; +} + +f(); +</pre> + +<h4 id="Ví_dụ_thực-tiễn">Ví dụ thực-tiễn</h4> + +<p>Internet Explorer 6 và 7 đều dùng thuật toán đếm tham chiếu cho DOM object. Vòng là lỗi thường gặp mà có thể gây ra rò rỉ bộ nhớ:</p> + +<pre class="brush: js">var div; +window.onload = function() { + div = document.getElementById('myDivElement'); + div.circularReference = div; + div.lotsOfData = new Array(10000).join('*'); +}; +</pre> + +<p>Trong ví dụ trên, phần tử DOM "myDivElement" có tham chiếu vòng tới chính nó trong thuộc tính "circularReference". Nếu thuộc tính đó không được loại bỏ hoặc gán giá trị null một cách tường minh, bộ thu thập rác (đếm tham chiếu) sẽ luôn có ít nhất một tham chiếu không bị thay đổi và sẽ giữ phần tử DOM trong bộ nhớ thậm chí khi nó đã bị loại bỏ khỏi cây DOM. Nếu phần tử DOM mang nhiều dữ liệu (thể hiện trong thuộc tính "lotsOfData"), bộ nhớ tiêu tốn bởi đống dữ liệu này sẽ không bao giờ được giải phóng.</p> + +<h3 id="Thuật_toán_Mark-and-sweep">Thuật toán Mark-and-sweep</h3> + +<p>Thuật toán này rút gọn định nghĩa từ "object không cần tới nữa" thành "object không thể tới được".</p> + +<p>Thuật toán này sử dụng tập các object gọi là <em>root</em> (Trong JavaScript, root là object toàn cục). Theo chu kỳ, bộ thu thập rác sẽ bắt đầu từ root, tìm tất cả object được tham chiếu tới từ root, rồi tới tất cả objects được tham chiếu từ các object trên, tương tự tới hết. Bắt đầu từ root, bộ thu thập rác sẽ tìm tất cả object <em>có thể tới được</em> và thu thập mọi object không thể tới được.</p> + +<p>Thuật toán này tốt hơn thuật toán trước bởi "object có không tham chiếu" dẫn tới object đó không thể tới được. Điều ngược lại không đúng như ví dụ về truy xuất vòng.</p> + +<p>Đến năm 2012, tất cả trình duyệt hiện đại đều hỗ trợ bộ thu thập rác mark-and-sweep. Mọi cải tiến trong bộ thu thập rác của JavaScript (generational/incremental/concurrent/parallel garbage collection) trong nhiều năm qua đều được áp dụng vào thuật toán này, nhưng không cải tiến thẳng vào thuật toán hay là rút gọn định nghĩa khi "object không cần tới nữa".</p> + +<h4 id="Vòng_không_còn_là_mối_lo">Vòng không còn là mối lo</h4> + +<p>Trong ví dụ đầu tiên, sau khi hàm gọi tới lệnh return, sẽ không còn object toàn cục nào tới được 2 object trong hàm. Vì thế, bộ thu thập rác sẽ xác định là chúng không thể tới được.</p> + +<h4 id="Hạn_chế_object_cần_phải_được_bất_khả_tới_một_cách_tường_minh">Hạn chế: object cần phải được bất khả tới một cách tường minh</h4> + +<p>Dù được đánh dấu là hạn chế, nhưng trên thực tế trường hợp này hiếm khi xảy ra nên không ai bận tâm tới bộ thu thập rác cả.</p> + +<h2 id="Xem_thêm">Xem thêm</h2> + +<ul> + <li><a class="external" href="http://www.ibm.com/developerworks/web/library/wa-memleak/">IBM article on "Memory leak patterns in JavaScript" (2007)</a></li> + <li><a class="external" href="http://msdn.microsoft.com/en-us/magazine/ff728624.aspx">Kangax article on how to register event handler and avoid memory leaks (2010)</a></li> + <li><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Performance" title="https://developer.mozilla.org/en-US/docs/Mozilla/Performance">Performance</a></li> +</ul> |