Java – Heap Space và Stack

Java Heap Space được sử dụng bởi Java Runtime để cấp phát bộ nhớ cho các object và các class của JRE. Bất cứ khi nào chúng ta tạo một object bất kỳ, nó luôn luôn được tạo trong Heap Space

Dữ liệu rác chạy trên bộ nhớ Heap để giải phóng bộ nhớ được sử dụng bởi các object mà không có bất kỳ tham chiếu nào. Bất kỳ object nào được tạo trong Heap Space đều có quyền truy cập global và có thể được tham chiếu từ bất kỳ nơi nào của ứng dụng.

Bộ nhớ java Stack được sử dụng để chạy một Thread. Chúng chứa các giá trị cụ thể của phương thức tồn tại trong thời gian ngắn và tham chiếu đến các object khác trong Heap mà được nhắm đến từ phương thức.

Bộ nhớ Stack thường được tham chiếu theo thứ tự LIFO (last-in-first-out). Bất cứ khi nào một phương thức được gọi, một khối mới được tạo trong bộ nhớ Stack giúp cho các phương thức có thể nắm giữ được các dữ liệu cũ nội tại và tham chiếu đến các object khác trong phương thức

Ngay khi phương thức kết thúc, các khối sẽ không được sử dụng và sẵn sàng cho các phương thức tiếp theo sử dụng. Kích thước của bộ nhớ Stack là nhỏ hơn nhiều so với bộ nhớ Heap

Bộ nhớ Heap và Stack trong chương trình Java

Để hiểu rõ hơn về cách sử dụng bộ nhớ Heap và Stack, hãy cùng xem chương trình sau

package com.example.test;

public class Memory {
  public static void main(String[] args) { // Line 1
    int i=1; // Line 2
    Object obj = new Object(); // Line 3
    Memory mem = new Memory(); // Line 4
    mem.foo(obj); // Line 5
  } // Line 9

  private void foo(Object param) { // Line 6
    String str = param.toString(); //// Line 7
    System.out.println(str);
  } // Line 8
}

Ảnh bên dưới đây miêu tả bộ nhớ Stack và Heap tương ứng với chương trình trên và cách chúng đang sử dụng để lưu trữ giá trị cơ bản, object và tham chiếu các biến

Hãy đi qua một lượt các bước chạy chương trình

  • Ngay khi chương trình chạy, nó sẽ tải tất cả các class đang chạy vào trong bộ nhớ Heap, khi phương thức main() được tìm thấy ở Line 1, Java Runtime sẽ tạo một bộ nhớ Stack được sử dụng bởi Thread chạy phương thức main().
  • Chúng ta đang tạo một biến local kiểu cơ bản tại Line 2, nên nó được tạo và lưu trong bộ nhớ Stack của phương thức main()
  • Vì chúng ta đang tạo một object tại Line 3 nên nó được tạo trong bộ nhớ Heap và bộ nhớ Stack chứa tham chiếu của nó. Một quá trình tương tự cũng xảy ra khi chúng ta tạo một Memory Object ở Line 4
  • Bây giờ khi chúng ta gọi phương thức food() ở Line 5, một khối ở trên đầu Stack được tạo và được sử dụng bởi phương thức food(). Do Java được truyền theo giá trị, một tham chiếu mới đến object được tạo trong khối Stack phương thức food() ở Line 6
  • String được tạo ở Line 7, nó đi vào trong String pool trong Heap space và một tham chiếu được tạo trong bộ nhớ Stack food() cho nó.
  • Phương thức food() kết thúc ở Line 8, lúc này khối bộ nhớ được cấp phát cho food() trong Stack được giải phóng
  • Ở Line 9, phương thức main() kết thúc và bộ nhớ Stack được tạo cho phương thức main() được huỷ. Ngoài ra do chương trình kết thúc tại Line này, do đó Java Runtime sẽ giải phóng hoàn toàn bộ nhớ và kết thúc việc chạy chương trình

Điểm khác nhau giữa bộ nhớ Stack và Heap Space

Dựa trên ví dụ trên chúng ta có thể tìm ra được điểm khác nhau giữa bộ nhớ Stack và Heap như sau:

  • Bộ nhớ Heap được sử dụng bởi tất cả các phần của ứng dụng trong khi bộ nhớ Stack chỉ được sử dụng bởi một Thread đang chạy
  • Bất cứ khi nào một object được tạo, nó luôn được lưu trữ trong Heap Space và bộ nhớ Stack chỉ chứa tham chiếu đến nó. Bộ nhớ Stack chỉ chứa các biến cơ bản local và các biến tham chiếu đến các object trong Heap Space
  • Các object được lưu trữ trong Heap Space có thể truy cập global trong khi bộ nhớ Stack không thể truy cập bởi các Thread khác.
  • Quản lý bộ nhớ trong Stack được thực hiện theo cách thức LIFO trong khi nó sẽ phức tạp hơn với bộ nhớ Heap bởi vì nó có thể truy cập Global. Bộ nhớ Heap được chia thành thế hệ trẻ, thế hệ già, vv.. chi tiết đọc thêm Java Garbage Collection.
  • Bộ nhớ Stack tồn tại trong thời gian ngắn trong khi bộ nhớ Head sẽ tồn tại từ lúc bắt đầu cho đến lúc kết thúc chạy chương trình
  • Chúng ta có thể sử dụng thêm lựa chọn JVM -Xms và -Xmx để định nghĩa kích thước khởi tạo và kích thước tối đa của bộ nhớ Heap. Chúng ta có thể sử dụng -Xss để định nghĩa kích thước bộ nhớ Stack
  • Khi bộ nhớ Stack đầy, Java Runtime sẽ đưa ra một lỗi java.lang.StackOverFlowError trong khi nếu bộ nhớ Heap đầy, nó sẽ đưa ra một lỗi java.lang.OutOfMemoryError: Java Heap Space 
  • Kích thước bộ nhớ Stack là rất ít so với bộ nhớ Heap. Do đơn giản trong việc cấp phát bộ nhớ (LIFO), bộ nhớ Stack rất nhanh khi so với bộ nhớ Heap.

 

You May Also Like

About the Author: Nguyen Dinh Thuc

Leave a Reply

Your email address will not be published.