CFSDN nhấn mạnh vào giá trị tạo ra nguồn mở và chúng tôi cam kết xây dựng nền tảng chia sẻ tài nguyên để mọi nhân viên CNTT có thể tìm thấy thế giới tuyệt vời của bạn tại đây.
Bài viết trên blog CFSDN Người phỏng vấn: New Object() trong Java chiếm bao nhiêu byte được tác giả thu thập và biên soạn. Nếu bạn quan tâm đến bài viết này, hãy nhớ thích nó.
Lời nói đầu
Hãy phân tích bố cục heap và bố cục của các đối tượng Java trong bộ nhớ.
con trỏ đối tượng
Trước tiên hãy xem xét một đoạn mã:
gói com.zwx.jvm; lớp công khai HeapMemory { Đối tượng riêng tư obj1 = Đối tượng mới(); tĩnh công khai void main(String[] args) { Đối tượng obj2 = Đối tượng mới(); }}
Trong đoạn mã trên, sự khác biệt giữa obj1 và obj2 trong bộ nhớ là gì?
Trước tiên, hãy nhớ lại những gì đã được đề cập trong bài viết JVM Series 1. Vùng phương thức lưu trữ cấu trúc của từng lớp, chẳng hạn như: nhóm hằng số thời gian chạy, dữ liệu thuộc tính và phương thức, cũng như dữ liệu như các phương thức và hàm tạo. Vì vậy, obj1 của chúng ta tồn tại trong vùng phương thức và new sẽ tạo một thể hiện đối tượng. Thể hiện đối tượng được lưu trữ trong heap, vì vậy chúng ta có hình ảnh sau (vùng phương thức trỏ đến heap):

Obj2 là một biến cục bộ trong phương thức và được lưu trữ trong bảng biến cục bộ trong khung ngăn xếp của ngăn xếp máy ảo Java. Đây là ngăn xếp cổ điển trỏ đến vùng heap:

Hãy suy nghĩ lại về điều đó. Một trong các biến của chúng ta trỏ đến vùng heap và vùng heap chỉ lưu trữ một đối tượng mẫu. Vậy làm thế nào đối tượng ví dụ trong vùng heap biết nó thuộc về Lớp nào? lớp nó tương ứng với thông tin meta thì sao? Điều này liên quan đến cách một đối tượng Java được sắp xếp trong bộ nhớ.
Mô hình bộ nhớ Java
Bộ nhớ đối tượng có thể được chia thành ba vùng: tiêu đề đối tượng (Header), dữ liệu cá thể (Dữ liệu cá thể) và phần đệm căn chỉnh (Đệm). Lấy hệ điều hành 64 bit làm ví dụ (khi tính năng nén con trỏ không được bật) Java. bố cục đối tượng như dưới đây Hiển thị:

Thông tin chi tiết về Mark Word trong tiêu đề đối tượng được mô tả chi tiết trong bài viết Nguyên tắc nâng cấp khóa đồng bộ hóa. Khoảng đệm căn chỉnh trong hình trên là không nhất thiết cần thiết. Nếu tổng của dữ liệu tiêu đề và phiên bản đối tượng chính xác là bội số của 8 byte thì không cần phải đệm căn chỉnh.
Bây giờ chúng ta đã biết cách bố trí bộ nhớ Java, hãy xem một câu hỏi phỏng vấn.
Đối tượng obj=new Object() chiếm byte
Đây là câu hỏi mà nhiều người trên Internet sẽ đề cập đến. Hãy phân tích nó dựa trên cách bố trí bộ nhớ Java ở trên làm ví dụ, kích thước mà new Object() chiếm giữ được chia thành hai trường hợp:
- Nếu không nén con trỏ, kích thước chiếm dụng là: 8(Mark Word)+8(Class Pointer)=16 byte
- Tính năng nén con trỏ được bật (mặc định được bật). Sau khi bật tính năng nén con trỏ, Con trỏ lớp sẽ được nén thành 4 byte và kích thước cuối cùng là: 8 (Mark Word) + 4 (Con trỏ lớp) + 4 (phần đệm căn chỉnh). ) = 16 byte
Đây có phải là kết quả? Hãy xác minh nó. Đầu tiên giới thiệu một phụ thuộc pom:
org.openjdk.jol jol-core 0.10
Sau đó tạo một bản demo đơn giản:
gói com.zwx.jvm; nhập org.openjdk.jol.info.ClassLayout; lớp công khai HeapMemory { public static void main(String[] args) { Đối tượng obj = new Object(); System.out.println(ClassLayout.parseInstance(obj).toPrintable()); }}
Đầu ra như sau:

Kết quả cuối cùng là 16 byte, không có vấn đề gì. Điều này là do tính năng nén con trỏ được bật theo mặc định, vì vậy hãy thử lại sau khi tắt tính năng nén con trỏ.
-XX:+UseCompressionOops bật tính năng nén con trỏ -XX:-UseCompressionOops tắt tính năng nén con trỏ

Chạy lại và nhận được kết quả như sau:

Như bạn có thể thấy, không có phần đệm căn chỉnh tại thời điểm này nhưng kích thước chiếm dụng vẫn là 16 bit.
Tiếp theo, hãy chứng minh kích thước của một đối tượng nếu nó có các thuộc tính.
Tạo một lớp mới chỉ có một thuộc tính byte bên trong:
gói com.zwx.jvm; lớp công khai MyItem { byte i = 0;}
Sau đó, xuất ra kích thước của lớp này tương ứng trong các trường hợp bật tính năng nén con trỏ và tắt tính năng nén con trỏ.
gói com.zwx.jvm; nhập org.openjdk.jol.info.ClassLayout; lớp công khai HeapMemory { công khai tĩnh void main(String[] args) { MyItem myItem = new MyItem(); System.out.println(ClassLayout.parseInstance(myItem).toPrintable()); }}
Bật tính năng nén con trỏ, chiếm 16 byte:

Tắt tính năng nén con trỏ, chiếm 24 byte:

Lúc này, bạn có thể thấy những ưu điểm của việc bật tính năng nén con trỏ. Nếu bạn tiếp tục tạo một số lượng lớn đối tượng, việc nén con trỏ vẫn sẽ tối ưu hóa hiệu suất.
Truy cập đối tượng
Sau khi tạo một đối tượng, tất nhiên chúng ta cần truy cập vào đối tượng đó, vậy khi cần truy cập vào một đối tượng thì chúng ta định vị đối tượng đó như thế nào? Hiện nay, có hai phương pháp truy cập đối tượng phổ biến nhất: truy cập xử lý và truy cập con trỏ trực tiếp.
Truy cập xử lý: Nếu sử dụng quyền truy cập xử lý, máy ảo Java sẽ phân bổ một bộ nhớ trong heap để lưu trữ nhóm xử lý, sau đó địa chỉ xử lý được lưu trữ trong đối tượng, sau đó dữ liệu cá thể đối tượng và địa chỉ dữ liệu loại đối tượng được lưu trữ trong vùng xử lý. xử lý hồ bơi.

Truy cập con trỏ trực tiếp (phương thức được sử dụng bởi máy ảo Hot Spot) Nếu sử dụng truy cập con trỏ trực tiếp, dữ liệu loại đối tượng sẽ được lưu trữ trực tiếp trong đối tượng.

So sánh giữa truy cập xử lý và truy cập con trỏ trực tiếp
Dễ dàng so sánh ở hình trên, tức là nếu bạn sử dụng một tay cầm để truy cập thì sẽ có thêm một vị trí con trỏ nữa, nhưng nó cũng có ưu điểm là nếu một đối tượng được di chuyển (địa chỉ thay đổi) thì bạn chỉ cần cần thay đổi con trỏ của nhóm điều khiển. Không sao, bạn không cần sửa đổi con trỏ trong đối tượng tham chiếu, nhưng nếu bạn sử dụng quyền truy cập con trỏ trực tiếp, bạn vẫn cần sửa đổi con trỏ tham chiếu trong bảng biến cục bộ.
Bộ nhớ đống
Chúng tôi đã đề cập ở trên rằng Mark Word trong tiêu đề đối tượng Java lưu trữ tuổi thế hệ của đối tượng, vậy tuổi thế hệ là gì?
Tuổi thế hệ của một đối tượng có thể hiểu là số lượng bộ sưu tập rác. Khi một đối tượng vẫn tồn tại sau một lần thu gom rác thì tuổi thế hệ sẽ tăng thêm 1. Trong máy ảo 64 bit, tuổi thế hệ chiếm 4 chữ số, và giá trị tối đa là 15. Tuổi thế hệ mặc định là 0000 và sẽ tăng dần theo số lượng bộ sưu tập rác.
Bộ nhớ heap Java được chia theo độ tuổi thế hệ và được chia thành vùng Trẻ và vùng Cũ. Việc phân bổ đối tượng trước tiên sẽ chuyển đến vùng Trẻ khi đạt đến độ tuổi thế hệ nhất định (-XX:MaxTenuringThreshold có thể đặt kích thước, mặc định. là 15), nó sẽ vào khu vực Cũ (Lưu ý: Nếu một đối tượng quá lớn, nó sẽ trực tiếp vào khu vực Cũ).
Lý do cho sự phân chia này là nếu toàn bộ heap chỉ có một vùng thì tất cả các đối tượng trong heap cần phải được quét mỗi lần trong quá trình thu gom rác, điều này gây lãng phí hiệu suất. Trên thực tế, vòng đời của hầu hết các đối tượng Java là rất ngắn. Một khi một đối tượng không thể tái chế được sau khi được tái chế nhiều lần thì có thể coi như nó có thể không được tái chế trong lần thu gom rác tiếp theo. và Khu cũ có thể thực hiện riêng. Chỉ khi khu Young vẫn không còn chỗ trống sau khi thu gom rác thì việc thu gom rác của Khu cũ mới được kích hoạt.

Quận Trẻ
Bây giờ nó được chia thành khu vực Young, chúng ta hãy xem cảnh sau Young là tổng quan sau khi thu gom rác:

Nếu một đối tượng xuất hiện ngay bây giờ và nó chiếm kích thước của hai đối tượng, bạn sẽ thấy rằng nó không thể được đặt nữa. Lúc này, GC (thu gom rác) sẽ được kích hoạt. Tuy nhiên, một khi GC (thu gom rác) được kích hoạt. có tác động đến các luồng của người dùng, vì để đảm bảo rằng các tham chiếu đối tượng không thay đổi liên tục trong quá trình GC, tất cả các luồng của người dùng cần phải dừng lại. Những điều này sẽ được giới thiệu chi tiết khi giải thích về việc thu gom rác trong bài viết tiếp theo nên chúng ta sẽ không đi sâu vào chi tiết ở đây.
Vì vậy, nhìn chung càng ít GC thì càng tốt. Trên thực tế, như bạn có thể thấy trong hình trên, có thể đặt ít nhất 3 đồ vật vào. Miễn là các đồ vật được sắp xếp theo thứ tự thì chúng có thể được đặt, vì vậy kết quả này là Có. rõ ràng là có không gian, nhưng do không gian không liên tục nên đối tượng không áp dụng được bộ nhớ, khiến GC bị kích hoạt.
Giải pháp là sắp xếp các đồ vật trong khu vực Young theo thứ tự nên một phương pháp đã được phát triển để chia khu vực Young lại thành 2 khu vực: khu vực Eden và khu vực Survivor.

Hoạt động cụ thể là: sau khi một đối tượng đến, trước tiên nó sẽ được phân bổ vào khu vực Eden. Khi khu vực Eden đã đầy, GC được kích hoạt Sau khi GC, để ngăn chặn sự gián đoạn của không gian, các đối tượng còn sót lại sẽ được sao chép vào khu vực Người sống sót. và sau đó khu vực Eden có thể được dọn sạch hoàn toàn. Tất nhiên, có một tiền đề cho điều này, đó là hầu hết các đồ vật đều có vòng đời cực kỳ ngắn. Về cơ bản, hầu hết các đồ vật trong khu vực Eden đều có thể được tái chế trong một lần thu gom rác ( tiền đề này có được sau khi thử nghiệm).
Khi GC được kích hoạt, khu vực Người sống sót cũng sẽ được tái chế cùng nhau. Điều đó không có nghĩa là chỉ có khu vực Eden được kích hoạt riêng biệt mà vấn đề này lại phát sinh. Khu vực Eden đảm bảo rằng không gian về cơ bản là liên tục, nhưng khu vực Người sống sót có thể tạo ra. các mảnh không gian, dẫn đến sự gián đoạn. Vì vậy, khu vực Survivor lại được chia thành hai:

Tại thời điểm này, quy trình làm việc lại trở nên như thế này: đầu tiên phân bổ không gian trong khu vực Eden, kích hoạt GC khi khu vực Eden đã đầy, sau đó sao chép các đối tượng còn sót lại vào khu vực S0 (khu vực S1 trống) sau GC và sau đó tiếp tục phân bổ các đối tượng trong khu vực Eden, sau khi kích hoạt lại GC, nếu thấy rằng khu vực S0 không thể chứa được (phân mảnh không gian được tạo ra, nhưng thực tế vẫn còn không gian), thì các đối tượng khu vực S0 sẽ được sao chép vào. khu vực S1. Và sao chép các đối tượng còn sót lại vào khu vực S1 Tại thời điểm này, khu vực S0 trống và lặp lại thao tác theo trình tự. Nếu đối tượng không gian khu vực S0 hoặc khu vực S1 vẫn không thể đặt được sau khi sao chép và di chuyển, điều đó có nghĩa là nó đã bị xóa. lúc này đã thực sự đầy đủ thì hãy đến khu người cao tuổi mượn một ít chỗ (đây là cơ chế đảm bảo. Khu người cao tuổi cần cung cấp bảo lãnh phân bổ không gian này nếu không còn đủ chỗ ở khu người cao tuổi thì sẽ có Full). được kích hoạt. GC, nếu vẫn chưa đủ, ngoại lệ OutOfMemeoyError sẽ được đưa ra.
Lưu ý: Để đảm bảo mỗi bản sao giữa S0 và S1 có thể diễn ra suôn sẻ, kích thước của S0 và S1 phải nhất quán và đồng thời phải để trống một vùng. Mặc dù cách tiếp cận này sẽ dẫn đến lãng phí không gian một chút nhưng nó đáng giá về mặt cải tiến hiệu suất khác.
Quận Cũ
Khi các đối tượng trong khu vực Trẻ đạt đến độ tuổi thế hệ đã đặt, các đối tượng sẽ vào khu vực Cũ. Khi khu vực Cũ đã đầy, GC đầy đủ sẽ được kích hoạt. Nếu vẫn không thể xóa khoảng trống, một ngoại lệ OutOfMemeoyError sẽ được đưa ra.
Biết chữ.
Có nhiều thuật ngữ mới được đề cập ở trên, nhưng trên thực tế, nhiều thuật ngữ trong số này có tên khác, tôi vẫn cảm thấy cần phải hiểu điều này.
Thu gom rác: viết tắt là GC.
Minor GC: GC dành cho thế hệ mới.
GC chính: Đối với GC ở thế hệ cũ, thông thường khi GC được kích hoạt ở thế hệ cũ thì GC nhỏ cũng được kích hoạt, tương đương với việc kích hoạt GC đầy đủ.
GC đầy đủ: GC xảy ra đồng thời ở thế hệ mới + thế hệ cũ.
Khu vực trẻ: thế hệ mới.
Khu vực cũ: tuổi già.
Quận Eden: Chưa tìm thấy bản dịch tiếng Trung nào (Vườn Địa Đàng?).
Vùng sống sót: Vùng sinh tồn.
S0 và S1: còn được gọi từ khu vực và đến khu vực Lưu ý rằng khu vực từ và đến liên tục trao đổi danh tính và S0 và S1 phải bằng nhau và một khu vực được đảm bảo trống.
Sơ đồ quỹ đạo cuộc đời của một đối tượng.
Từ phần giới thiệu ở trên, bạn sẽ có ấn tượng chung rằng một vật thể sẽ tiếp tục lưu hành trong khu vực Eden, khu vực S0, khu vực S1 và khu vực Cũ (tất nhiên, ngoại trừ những đồ vật có thời gian sử dụng ngắn sẽ được tái chế ngay từ đầu) , chúng ta có thể nhận được một biểu đồ luồng sau:

Tóm tắt
Bài viết này chủ yếu giới thiệu cách một đối tượng Java được lưu trữ trong heap và kết hợp cách bố trí bộ nhớ của các đối tượng Java để thể hiện kích thước của một đối tượng thông thường. Nó cũng phân tích sự phân chia không gian trong heap và lý do phân chia. Các kiến thức liên quan đến GC không được giải thích chuyên sâu như GC, thuật toán GC và GC Collector sẽ được phân tích chi tiết trong bài viết tiếp theo.
Đến đây là kết thúc bài viết về Người phỏng vấn: New Object() trong Java chiếm bao nhiêu byte? Để biết thêm thông tin về Java new Object() byte, vui lòng tìm kiếm các bài viết trước của tôi hoặc tiếp tục duyệt qua các bài viết liên quan. Tôi hy vọng bạn sẽ ủng hộ tôi nhiều hơn. trong tương lai! .
Cuối cùng, bài viết này về người phỏng vấn: new Object() trong Java chiếm bao nhiêu byte? Nếu bạn muốn biết thêm về người phỏng vấn: new Object() trong Java chiếm bao nhiêu byte? Các bài viết của CFSDN hoặc tiếp tục duyệt các bài viết liên quan, tôi hy vọng bạn sẽ ủng hộ blog của tôi trong tương lai! .
Tôi là một lập trình viên xuất sắc, rất giỏi!