Trường nguyên thủy được lưu trữ ở đâu?
Các trường nguyên thủy được lưu trữ như một phần của đối tượng được khởi tạo ở đâu đó . Cách dễ nhất để nghĩ về nơi này - là đống. Tuy nhiên , điều này không phải lúc nào cũng đúng. Như được mô tả trong lý thuyết và thực tiễn Java: Truyền thuyết hiệu suất đô thị, được xem xét lại :
Các JVM có thể sử dụng một kỹ thuật gọi là phân tích thoát, qua đó họ có thể nói rằng các đối tượng nhất định vẫn bị giới hạn trong một luồng duy nhất trong suốt vòng đời của chúng và thời gian tồn tại đó bị giới hạn bởi thời gian tồn tại của một khung ngăn xếp nhất định. Các đối tượng như vậy có thể được phân bổ an toàn trên ngăn xếp thay vì đống. Thậm chí tốt hơn, đối với các đối tượng nhỏ, JVM có thể tối ưu hóa hoàn toàn việc phân bổ và chỉ cần đưa các trường của đối tượng vào các thanh ghi.
Do đó, ngoài việc nói "đối tượng được tạo ra và trường cũng ở đó", người ta không thể nói nếu một cái gì đó nằm trên đống hoặc trên ngăn xếp. Lưu ý rằng đối với các đối tượng nhỏ, tồn tại ngắn, có thể 'đối tượng' sẽ không tồn tại trong bộ nhớ như vậy và thay vào đó có thể có các trường của nó được đặt trực tiếp trong các thanh ghi.
Bài viết kết luận với:
Các JVM rất tốt trong việc tìm ra những thứ mà chúng ta từng cho rằng chỉ có nhà phát triển mới có thể biết. Bằng cách để JVM chọn giữa phân bổ ngăn xếp và phân bổ heap trên cơ sở từng trường hợp cụ thể, chúng ta có thể nhận được các lợi ích hiệu năng của phân bổ ngăn xếp mà không khiến lập trình viên thống nhất về việc phân bổ trên ngăn xếp hay trên heap.
Vì vậy, nếu bạn có mã trông giống như:
void foo(int arg) {
Bar qux = new Bar(arg);
...
}
trong trường hợp ...
không cho phép qux
rời khỏi phạm vi đó, qux
có thể được phân bổ trên ngăn xếp thay thế. Đây thực sự là một chiến thắng cho VM vì điều đó có nghĩa là nó không cần phải được thu gom rác - nó sẽ biến mất khi rời khỏi phạm vi.
Thêm về phân tích thoát tại Wikipedia. Đối với những người sẵn sàng đi sâu vào các bài báo, Phân tích thoát cho Java từ IBM. Đối với những người đến từ thế giới C #, bạn có thể thấy Ngăn xếp là chi tiết triển khai và Sự thật về các loại giá trị của Eric Lippert đọc tốt (chúng cũng hữu ích cho các loại Java vì nhiều khái niệm và khía cạnh giống nhau hoặc tương tự nhau) . Tại sao sách .Net nói về stack vs phân bổ bộ nhớ heap? cũng đi vào đây
Trên các đống của đống và đống
Trên đống
Vì vậy, tại sao có ngăn xếp hoặc đống ở tất cả? Đối với những thứ rời khỏi phạm vi, ngăn xếp có thể tốn kém. Hãy xem xét mã:
void foo(String arg) {
bar(arg);
...
}
void bar(String arg) {
qux(arg);
...
}
void qux(String arg) {
...
}
Các tham số là một phần của ngăn xếp quá. Trong trường hợp bạn không có một đống, bạn sẽ chuyển toàn bộ giá trị trên ngăn xếp. Điều này tốt cho "foo"
các chuỗi nhỏ ... nhưng điều gì sẽ xảy ra nếu ai đó đặt một tệp XML lớn trong chuỗi đó. Mỗi cuộc gọi sẽ sao chép toàn bộ chuỗi lớn vào ngăn xếp - và điều đó sẽ khá lãng phí.
Thay vào đó, tốt hơn là đặt các đối tượng có một số cuộc sống bên ngoài phạm vi ngay lập tức (được chuyển sang phạm vi khác, bị mắc kẹt trong một cấu trúc mà người khác đang duy trì, v.v ...) vào một khu vực khác được gọi là đống.
Trên ngăn xếp
Bạn không cần ngăn xếp. Theo giả thuyết, người ta có thể viết một ngôn ngữ không sử dụng ngăn xếp (độ sâu tùy ý). Một BASIC cũ mà tôi học được khi còn trẻ đã làm như vậy, một người chỉ có thể thực hiện 8 cấp độ gosub
cuộc gọi và tất cả các biến là toàn cầu - không có ngăn xếp.
Ưu điểm của ngăn xếp là khi bạn có một biến tồn tại với một phạm vi, khi bạn rời khỏi phạm vi đó, khung ngăn xếp đó được bật lên. Nó thực sự đơn giản hóa những gì ở đó và những gì không có ở đó. Chương trình chuyển sang thủ tục khác, khung stack mới; chương trình trở lại quy trình và bạn đã quay lại chương trình thấy phạm vi hiện tại của mình; chương trình rời khỏi thủ tục và tất cả các mục trên ngăn xếp được sắp xếp lại.
Điều này thực sự làm cho cuộc sống dễ dàng cho người viết thời gian chạy cho mã để sử dụng một ngăn xếp và một đống. Họ chỉ đơn giản là nhiều khái niệm và cách làm việc với mã cho phép người viết mã bằng ngôn ngữ được giải phóng khỏi suy nghĩ của họ một cách rõ ràng.
Bản chất của ngăn xếp cũng có nghĩa là nó không thể bị phân mảnh. Phân mảnh bộ nhớ là một vấn đề thực sự với heap. Bạn phân bổ một vài đối tượng, sau đó rác thu thập một đối tượng ở giữa, và sau đó thử tìm không gian cho đối tượng lớn tiếp theo được phân bổ. Đó là một mớ hỗn độn. Thay vào đó, có thể đặt mọi thứ lên ngăn xếp có nghĩa là bạn không phải đối phó với điều đó.
Khi một cái gì đó là rác được thu thập
Khi một cái gì đó là rác được thu thập, nó biến mất. Nhưng nó chỉ là rác được thu thập vì nó đã bị quên - không có thêm tài liệu tham khảo nào cho đối tượng trong chương trình có thể được truy cập từ trạng thái hiện tại của chương trình.
Tôi sẽ chỉ ra rằng đây là một sự đơn giản hóa rất lớn của bộ sưu tập rác. Có nhiều trình thu gom rác (ngay cả trong Java - bạn có thể điều chỉnh trình thu gom rác bằng cách sử dụng các cờ ( tài liệu ) khác nhau. Chúng hoạt động khác nhau và các sắc thái về cách mỗi người làm mọi thứ quá sâu cho câu trả lời này. Bạn có thể muốn đọc Khái niệm cơ bản về bộ sưu tập rác Java để hiểu rõ hơn về cách thức hoạt động của một số thứ đó.
Điều đó nói rằng, nếu một cái gì đó được phân bổ trên ngăn xếp, thì đó không phải là rác được thu thập như một phần của System.gc()
- nó được xử lý khi khung ngăn xếp bật lên. Nếu một cái gì đó trên đống, và được tham chiếu từ một cái gì đó trên ngăn xếp, nó sẽ không phải là rác được thu thập tại thời điểm đó.
Vì sao vấn đề này?
Đối với hầu hết các phần, truyền thống của nó. Các sách văn bản được viết và các lớp trình biên dịch và các tài liệu bit khác nhau tạo ra một vấn đề lớn về heap và stack.
Tuy nhiên, các máy ảo ngày nay (JVM và tương tự) đã cố gắng hết sức để cố gắng giấu điều này khỏi lập trình viên. Trừ khi bạn đang hết cái này hay cái kia và cần biết tại sao (thay vì chỉ tăng không gian lưu trữ một cách thích hợp), điều đó không thành vấn đề quá nhiều.
Đối tượng ở đâu đó và ở nơi nó có thể được truy cập chính xác và nhanh chóng trong khoảng thời gian thích hợp mà nó tồn tại. Nếu nó nằm trên chồng hoặc đống - nó không thực sự quan trọng.