Stack và Heap trong Java
Stack và Heap trong Java
Đối với các lập trình viên,
không chỉ hoàn thiện chương trình là đủ, chúng ta còn phải làm cho chương trình
có hiệu suất tốt hơn: làm chương trình chạy thời gian ngắn hơn, tốt ít bộ nhớ hơn.
Vấn đề về bộ nhớ chính là một bài toán hiệu suất mà các nhà lập trình phải giải
quyết nó một cách tối ưu nhất. Để giải quyết về vấn đề này, chúng ta cần phải
hiểu rõ về bộ nhớ trên máy tính, đặc biệt là việc sử dụng bộ nhớ của nó.
Ô nhớ hiểu đơn giản là một
đơn vị để lưu trữ dữ liệu. Tập hợp nhiều ô nhớ được gọi là bộ nhớ (hoặc vùng bộ
nhớ). Trong Java, vì là một ngôn ngữ ra đời cải tiến hơn so với các ngôn ngữ trước,
nên việc phân chia vùng nhớ và xử lý chúng không đơn giản chỉ xoay quanh về khái
niệm bộ nhớ.
Bộ nhớ trong Java được
chia làm 2 vùng: Stack và Heap.
Stack được
xem là vùng bộ nhớ để thực thi luồng. Chúng lưu trữ các biến cục bộ, các tham số
của phương thức. Chúng hoạt động như tên gọi của nó (Stack – chồng chất), biến đầu tiên sẽ nằm ở dưới cùng, các biến sau
sẽ chồng lên biến trước và cứ thế lên trên cùng.
Heap là
bộ nhớ để lưu trữ các đối tượng thực sự. Các object khi khởi tạo thì bộ nhớ của
nó sẽ nằm trong Heap, còn bản thân nó
là một địa chỉ lưu ở Stack và truy cập
đến biến trong Heap. Ví dụ ta có Dog
a = new Dog(), thì a mang giá trị là địa chỉ đi đến bộ nhớ thực sự của nó, nơi
mà các thuộc tính được lưu bên trong nó.
Để hiểu rõ hơn cách Stack và Heap hoạt động, ta sẽ làm ví dụ như sau (vừa nhìn hình vừa đọc tiếp nhé):
Chúng ta tạo ra 1 class Dog, và một hàm main dùng để
xuất ra name của class Dog. Ở đây, khi hàm main chạy, 1 luồng sinh ra, khi các
biến khởi tạo trong luồng này, nó sẽ lưu trong Stack. Quy trình chạy theo các bước
1. Ban
đầu, chúng ta khai báo 1 biến t là kiểu số nguyên, biến này sẽ tạo trong bộ nhớ
Stack.
2. Tiếp
đó, chúng ta khai báo 1 biến aDog kiểu Dog, biến này sẽ được lưu trong bộ nhớ Stack.
3. Khi
chúng ta khởi tạo bằng toán tử new và truyền tham số vào, lúc này các bộ nhớ
trong vùng Heap sẽ cung cấp cho đối
tượng này, cụ thể là cung cấp cho các thuộc tính của đối tượng này. Khi chạy hết
hàm Constructor, các giá trị sẽ đưa vào các thuộc tính trên. Cuối cùng, biến
aDog sẽ lưu trữ địa chỉ trỏ tới vùng nhớ đó.
4. Bắt
đầu thực hiện gọi hàm viewName() và truyền tham số aDog vào.
5. Trong
viewName(), biến thisDog được sinh ra tại Stack,
và được truyền địa chỉ từ aDog vào, nên cả aDog và thisDog đều mang cùng 1 địa
chỉ trỏ tới Object đã khởi tạo trước đó.
6. Biến
String được khai báo, và nó cũng lưu địa chỉ tại Stack, trỏ đến vị trí chứa dữ liệu là name của thisDog.
Vì
String là một dạng dữ liệu dặc biệt, không phải kiểu nguyên thủy, và cách tổ chức
bộ nhớ cũng khác so với các kiểu dữ liệu khác, nên mình chỉ nói một cách đơn giản
nhất thôi
7. Class
System gọi hàm out.print() ra làm việc. Hàm trên làm việc và xuất ra Console
name của thisDog.
Tuy có vẻ khó hiểu nhưng thực sự thì khó hiểu thật *cười.
Có một số khuất mắt mình vẫn không thể giải đáp được, ví dụ như là int t khi chưa
gán giá trị thì có khởi tạo vùng nhớ hay không, và nguyên tắc sử dụng bộ nhớ của
String. Tuy nhiên, vấn đề cơ bản về Stack
và Heap cũng đã được giải quyết ổn
thỏa. Phù!
Nội dung bài viết thuộc
về Lê Công Diễn.
Người
viết: Lê Công Diễn
Mang
đi nhớ ghi nguồn
Nhận xét
Đăng nhận xét