Các mảng được lưu trữ trong bộ nhớ javascript như thế nào?
Hầu hết thời gian, bạn có thể không biết gì về quản lý bộ nhớ với tư cách là nhà phát triển JavaScript. Rốt cuộc, công cụ JavaScript xử lý việc này cho bạn Show
Tuy nhiên, lúc này hay lúc khác, bạn sẽ gặp phải các sự cố, chẳng hạn như rò rỉ bộ nhớ, mà bạn chỉ có thể giải quyết nếu biết cách cấp phát bộ nhớ hoạt động Trong bài viết này, tôi sẽ giới thiệu cho bạn cách hoạt động của cấp phát bộ nhớ và thu gom rác cũng như cách bạn có thể tránh một số lỗi rò rỉ bộ nhớ phổ biến
Vòng đời bộ nhớTrong JavaScript, khi chúng ta tạo các biến, hàm hoặc bất kỳ thứ gì bạn có thể nghĩ đến, công cụ JS sẽ phân bổ bộ nhớ cho việc này và giải phóng nó khi không cần thiết nữa Cấp phát bộ nhớ là quá trình dự trữ không gian trong bộ nhớ, trong khi giải phóng bộ nhớ sẽ giải phóng không gian, sẵn sàng để sử dụng cho mục đích khác Mỗi khi chúng ta gán một biến hoặc tạo một hàm, bộ nhớ cho biến đó luôn trải qua các giai đoạn sau
Bộ nhớ heap và stackBây giờ chúng tôi biết rằng đối với mọi thứ chúng tôi xác định trong JavaScript, công cụ sẽ phân bổ bộ nhớ và giải phóng nó khi chúng tôi không cần nó nữa Câu hỏi tiếp theo nảy ra trong đầu tôi là. Cái này sẽ được lưu trữ ở đâu? Công cụ JavaScript có hai nơi để lưu trữ dữ liệu. Bộ nhớ heap và stack Heaps và stacks là hai cấu trúc dữ liệu mà engine sử dụng cho các mục đích khác nhau Cây rơm. Cấp phát bộ nhớ tĩnh
Tất cả các giá trị được lưu trữ trong ngăn xếp vì tất cả chúng đều chứa các giá trị nguyên thủy Ngăn xếp là cấu trúc dữ liệu mà JavaScript sử dụng để lưu trữ dữ liệu tĩnh. Dữ liệu tĩnh là dữ liệu mà công cụ biết kích thước tại thời điểm biên dịch. Trong JavaScript, điều này bao gồm các giá trị nguyên thủy (chuỗi, số, booleans, không xác định và null) và các tham chiếu trỏ đến các đối tượng và hàm Vì công cụ biết rằng kích thước sẽ không thay đổi nên nó sẽ phân bổ một lượng bộ nhớ cố định cho mỗi giá trị Quá trình cấp phát bộ nhớ ngay trước khi thực thi được gọi là cấp phát bộ nhớ tĩnh Vì công cụ phân bổ một lượng bộ nhớ cố định cho các giá trị này, nên có giới hạn về mức độ lớn của các giá trị nguyên thủy. Giới hạn của các giá trị này và toàn bộ ngăn xếp khác nhau tùy thuộc vào trình duyệt đống. Cấp phát bộ nhớ độngHeap là một không gian khác để lưu trữ dữ liệu nơi JavaScript lưu trữ các đối tượng và chức năng Không giống như ngăn xếp, công cụ không phân bổ lượng bộ nhớ cố định cho các đối tượng này. Thay vào đó, nhiều không gian hơn sẽ được phân bổ khi cần thiết Cấp phát bộ nhớ theo cách này còn được gọi là cấp phát bộ nhớ động Để có cái nhìn tổng quan, dưới đây là các tính năng của hai kho được so sánh cạnh nhau StackHeap Các giá trị và tham chiếu nguyên thủyCác đối tượng và hàmKích thước được biết tại thời điểm biên dịchKích thước được biết tại thời điểm chạyPhân bổ một lượng bộ nhớ cố địnhKhông giới hạn cho mỗi đối tượngví dụChúng ta hãy xem xét một vài mã ví dụ. Trong chú thích tôi đề cập đến những gì đang được phân bổ
JS cấp phát bộ nhớ cho đối tượng này trong heap. Các giá trị thực tế vẫn còn nguyên thủy, đó là lý do tại sao chúng được lưu trữ trong ngăn xếp
Mảng cũng là đối tượng, đó là lý do tại sao chúng được lưu trữ trong heap
Các giá trị nguyên thủy là bất biến, có nghĩa là thay vì thay đổi giá trị ban đầu, JavaScript sẽ tạo một giá trị mới Tài liệu tham khảo trong JavaScriptTất cả các biến đầu tiên trỏ đến ngăn xếp. Trong trường hợp đó là giá trị không nguyên thủy, ngăn xếp chứa tham chiếu đến đối tượng trong heap Bộ nhớ của heap không được sắp xếp theo bất kỳ cách cụ thể nào, đó là lý do tại sao chúng ta cần giữ một tham chiếu đến nó trong ngăn xếp. Bạn có thể coi các tham chiếu là địa chỉ và các đối tượng trong heap là những ngôi nhà mà các địa chỉ này thuộc về.
Trong hình này, chúng ta có thể quan sát cách các giá trị khác nhau được lưu trữ. Lưu ý cách cả 2 và 3 đều trỏ đến cùng một đối tượngví dụ
Điều này tạo ra một đối tượng mới trong đống và một tham chiếu đến nó trong ngăn xếp
Thu gom rác thảiBây giờ chúng ta biết cách JavaScript phân bổ bộ nhớ cho tất cả các loại đối tượng, nhưng nếu chúng ta nhớ vòng đời của bộ nhớ, thì còn thiếu một bước cuối cùng. giải phóng bộ nhớ Giống như cấp phát bộ nhớ, công cụ JavaScript cũng xử lý bước này cho chúng tôi. Cụ thể hơn, bộ thu gom rác đảm nhiệm việc này Khi công cụ JavaScript nhận ra rằng một biến hoặc hàm đã cho không còn cần thiết nữa, nó sẽ giải phóng bộ nhớ mà nó đã chiếm Vấn đề chính của vấn đề này là liệu bộ nhớ có còn cần thiết hay không là một vấn đề không thể giải quyết được, điều đó có nghĩa là không thể có một thuật toán nào có thể thu thập tất cả bộ nhớ không cần thiết nữa vào đúng thời điểm nó trở nên lỗi thời Một số thuật toán cung cấp một xấp xỉ tốt cho vấn đề. Tôi sẽ thảo luận về những cái được sử dụng nhiều nhất trong phần này. Bộ sưu tập rác đếm tham chiếu và thuật toán đánh dấu và quét Bộ sưu tập rác đếm tham chiếuĐây là phép tính gần đúng dễ nhất. Nó thu thập các đối tượng không có tham chiếu trỏ đến chúng Hãy cùng xem ví dụ sau. Các dòng đại diện cho tài liệu tham khảo Lưu ý rằng trong khung cuối cùng chỉ có 4 nằm trong đống vì nó là đối tượng có tham chiếu cuối cùngchu kỳVấn đề với thuật toán này là nó không xem xét các tham chiếu tuần hoàn. Điều này xảy ra khi một hoặc nhiều đối tượng tham chiếu lẫn nhau nhưng không thể truy cập chúng thông qua mã nữa
Vì các đối tượng 5 và 6 tham chiếu lẫn nhau nên thuật toán sẽ không giải phóng bộ nhớ đã cấp phát. Không còn cách nào để chúng tôi truy cập vào hai đối tượng nữaĐặt chúng thành 7 sẽ không làm cho thuật toán đếm tham chiếu nhận ra rằng chúng không thể được sử dụng nữa vì cả hai đều có tham chiếu đếnThuật toán đánh dấu và quétThuật toán đánh dấu và quét có giải pháp cho các phụ thuộc theo chu kỳ. Thay vì chỉ đếm các tham chiếu đến một đối tượng nhất định, nó sẽ phát hiện xem chúng có thể truy cập được từ đối tượng gốc hay không Root trong trình duyệt là đối tượng 8, trong khi ở NodeJS, đây là 9Thuật toán đánh dấu các đối tượng không thể truy cập là rác và quét (thu thập) chúng sau đó. Các đối tượng gốc sẽ không bao giờ được thu thập Bằng cách này, sự phụ thuộc theo chu kỳ không còn là vấn đề nữa. Trong ví dụ trước đó, cả đối tượng 6 và 5 đều không thể truy cập được từ thư mục gốc. Như vậy, cả hai sẽ được đánh dấu là rác và được thu gomKể từ năm 2012, thuật toán này được triển khai trong tất cả các trình duyệt hiện đại. Các cải tiến chỉ được thực hiện đối với hiệu suất và triển khai chứ không phải ý tưởng cốt lõi của thuật toán đánh đổiThu gom rác tự động cho phép chúng ta tập trung vào việc xây dựng ứng dụng thay vì mất thời gian quản lý bộ nhớ. Tuy nhiên, có một số sự đánh đổi mà chúng ta cần lưu ý Sử dụng bộ nhớDo các thuật toán không thể biết chính xác khi nào bộ nhớ sẽ không cần thiết nữa, các ứng dụng JavaScript có thể sử dụng nhiều bộ nhớ hơn mức thực sự cần Mặc dù các đối tượng được đánh dấu là rác, bộ thu gom rác quyết định khi nào và liệu bộ nhớ được cấp phát có được thu thập hay không Nếu bạn cần ứng dụng của mình có bộ nhớ hiệu quả nhất có thể, tốt hơn hết bạn nên sử dụng ngôn ngữ cấp thấp hơn. Nhưng hãy nhớ rằng điều này đi kèm với sự đánh đổi của chính nó Màn biểu diễnCác thuật toán thu gom rác cho chúng tôi thường chạy định kỳ để dọn dẹp các đối tượng không sử dụng Vấn đề với điều này là chúng tôi, những nhà phát triển, không biết chính xác khi nào điều này sẽ xảy ra. Thu gom nhiều rác hoặc thu gom rác thường xuyên có thể ảnh hưởng đến hiệu suất vì nó cần một lượng sức mạnh tính toán nhất định để làm như vậy Tuy nhiên, tác động thường không đáng chú ý đối với người dùng hoặc nhà phát triển Rò rỉ bộ nhớĐược trang bị tất cả những kiến thức này về quản lý bộ nhớ, chúng ta hãy xem xét các rò rỉ bộ nhớ phổ biến nhất Bạn sẽ thấy rằng những điều này có thể dễ dàng tránh được nếu một người hiểu những gì đang diễn ra đằng sau hậu trường biến toàn cầuLưu trữ dữ liệu trong các biến toàn cục có lẽ là kiểu rò rỉ bộ nhớ phổ biến nhất Ví dụ, trong trình duyệt, nếu bạn sử dụng 2 thay vì 3 hoặc 4—hoặc loại bỏ hoàn toàn từ khóa—công cụ sẽ gắn biến vào đối tượng 8Điều tương tự cũng xảy ra với các hàm được xác định bằng từ khóa 6 0Cả 3 biến 7, 8, và 9 sẽ được gắn vào đối tượng 8Điều này chỉ áp dụng cho các biến và chức năng được xác định trong phạm vi toàn cầu. Nếu bạn muốn tìm hiểu thêm về điều này, hãy xem bài viết này giải thích phạm vi JavaScript Tránh điều này bằng cách chạy mã của bạn ở chế độ nghiêm ngặt Ngoài việc vô tình thêm các biến vào thư mục gốc, có nhiều trường hợp bạn có thể cố tình làm điều này Bạn chắc chắn có thể sử dụng các biến toàn cục, nhưng hãy đảm bảo rằng bạn giải phóng dung lượng khi không cần dữ liệu nữa Để giải phóng bộ nhớ, hãy gán biến toàn cục cho 7 6
Quên hẹn giờ và gọi lạiQuên bộ hẹn giờ và gọi lại có thể làm tăng mức sử dụng bộ nhớ của ứng dụng. Đặc biệt trong Ứng dụng một trang (SPA), bạn phải cẩn thận khi thêm trình xử lý sự kiện và gọi lại một cách linh hoạt Đồng hồ hẹn giờ bị lãng quên 7Đoạn mã trên chạy chức năng cứ sau 2 giây. Nếu bạn có mã như thế này trong dự án của mình, bạn có thể không cần mã này để chạy mọi lúc Các đối tượng được tham chiếu trong khoảng thời gian sẽ không bị thu gom rác miễn là khoảng thời gian đó không bị hủy bỏ Đảm bảo xóa khoảng thời gian khi không cần thiết nữa 8Điều này đặc biệt quan trọng trong các SPA. Ngay cả khi điều hướng khỏi trang cần khoảng thời gian này, nó vẫn sẽ chạy ở chế độ nền cuộc gọi lại bị lãng quênGiả sử bạn thêm một trình nghe 2 vào một nút, nút này sau đó sẽ bị xóaCác trình duyệt cũ không thể thu thập trình nghe, nhưng ngày nay, đây không còn là vấn đề nữa Tuy nhiên, bạn nên xóa trình xử lý sự kiện khi bạn không cần chúng nữa 0Ngoài tham chiếu DOMRò rỉ bộ nhớ này tương tự như những lần trước. Nó xảy ra khi lưu trữ các phần tử DOM trong JavaScript 0Khi bạn xóa bất kỳ phần tử nào trong số đó, có thể bạn cũng muốn đảm bảo xóa luôn phần tử này khỏi mảng Nếu không, các phần tử DOM này không thể được thu thập 1Xóa phần tử khỏi mảng giúp nó đồng bộ với DOM Vì mọi phần tử DOM cũng giữ một tham chiếu đến nút cha của nó, nên bạn sẽ ngăn trình thu gom rác thu thập cha và con của phần tử Sự kết luậnTrong bài viết này, tôi đã tóm tắt các khái niệm cốt lõi về quản lý bộ nhớ trong JavaScript Viết bài viết này đã giúp tôi làm sáng tỏ một số khái niệm mà tôi không hiểu hoàn toàn và tôi hy vọng điều này sẽ đóng vai trò là một cái nhìn tổng quan về cách hoạt động của quản lý bộ nhớ trong JavaScript Tôi đã học được điều này từ một số bài báo tuyệt vời khác mà tôi cũng muốn đề cập ở đây
Các bài viết khác có thể bạn quan tâm
Nếu bạn muốn có nhiều bài viết như thế này, hãy để lại tin nhắn cho tôi và đảm bảo rằng bạn đã đăng ký nhận bản tin email của tôi Dữ liệu mảng được lưu trữ trong bộ nhớ như thế nào?Mảng lưu trữ các phần tử của nó ở các vị trí bộ nhớ liền kề . Nếu bạn đã tạo mảng cục bộ thì nó sẽ nằm trên ngăn xếp. Nơi các phần tử được lưu trữ tùy thuộc vào đặc tả lưu trữ.
Mảng JavaScript được lưu trữ như thế nào?Các đối tượng mảng JavaScript có thể được lưu trữ trong các biến và được xử lý giống như cách bạn xử lý với bất kỳ loại dữ liệu nào khác. Sự khác biệt là chúng ta có thể truy cập từng giá trị trong danh sách một cách riêng lẻ và thực hiện các hoạt động khác nhau, chẳng hạn như lặp qua nó.
Làm thế nào là các phần tử mảng được lưu trữ trong vị trí bộ nhớ?Các phần tử mảng luôn được lưu trữ trong các vị trí bộ nhớ tuần tự . Do đó, câu trả lời đúng là phương án (A)
Các đối tượng JavaScript được lưu trữ trong bộ nhớ như thế nào?“Các biến trong JavaScript (và hầu hết các ngôn ngữ lập trình khác) được lưu trữ ở hai nơi. ngăn xếp và đống . Ngăn xếp thường là một vùng bộ nhớ liên tục phân bổ ngữ cảnh cục bộ cho từng chức năng thực thi. Heap là một vùng lớn hơn nhiều lưu trữ mọi thứ được phân bổ động. |