Đây là một khiếu nại lâu dài với Java, nhưng phần lớn là vô nghĩa và thường dựa trên việc xem xét thông tin sai. Các cụm từ thông thường là một cái gì đó như "Hello World trên Java mất 10 megabyte! Tại sao nó cần điều đó?" Chà, đây là một cách để đưa Hello World vào một yêu cầu JVM 64 bit để chiếm hơn 4 gigabyte ... ít nhất là bằng một hình thức đo lường.
java -Xms1024m -Xmx4096m com.example.Hello
Những cách khác nhau để đo bộ nhớ
Trên Linux, lệnh trên cùng cung cấp cho bạn một số số khác nhau cho bộ nhớ. Dưới đây là những gì nó nói về ví dụ Hello World:
NGƯỜI DÙNG PR PR VI VIRT RES SHR S% CPU% MEM TIME + HÀNH
2120 kgregory 20 0 4373m 15m 7152 S 0 0.2 0: 00.10 java
- VIRT là không gian bộ nhớ ảo: tổng của mọi thứ trong bản đồ bộ nhớ ảo (xem bên dưới). Nó phần lớn là vô nghĩa, trừ khi nó không (xem bên dưới).
- RES là kích thước cài đặt thường trú: số lượng trang hiện đang cư trú trong RAM. Trong hầu hết các trường hợp, đây là con số duy nhất mà bạn nên sử dụng khi nói "quá lớn". Nhưng nó vẫn không phải là một con số rất tốt, đặc biệt là khi nói về Java.
- SHR là dung lượng bộ nhớ lưu trú được chia sẻ với các quy trình khác. Đối với một quy trình Java, điều này thường được giới hạn ở các thư viện dùng chung và các tệp JARfiles được ánh xạ bộ nhớ. Trong ví dụ này, tôi chỉ có một quy trình Java đang chạy, vì vậy tôi nghi ngờ rằng 7k là kết quả của các thư viện được HĐH sử dụng.
- SWAP không được bật theo mặc định và không được hiển thị ở đây. Nó cho biết dung lượng bộ nhớ ảo hiện đang tồn tại trên đĩa, cho dù nó có thực sự nằm trong không gian trao đổi hay không . HĐH rất tốt trong việc giữ các trang hoạt động trong RAM và cách chữa trị duy nhất để hoán đổi là (1) mua thêm bộ nhớ hoặc (2) giảm số lượng tiến trình, vì vậy tốt nhất nên bỏ qua con số này.
Tình huống cho Windows Task Manager phức tạp hơn một chút. Trong Windows XP, có các cột "Sử dụng bộ nhớ" và "Kích thước bộ nhớ ảo", nhưng tài liệu chính thức im lặng về ý nghĩa của chúng. Windows Vista và Windows 7 thêm nhiều cột và chúng thực sự được ghi lại . Trong số này, phép đo "Bộ làm việc" là hữu ích nhất; nó gần tương ứng với tổng số RES và SHR trên Linux.
Hiểu bản đồ bộ nhớ ảo
Bộ nhớ ảo được tiêu thụ bởi một tiến trình là tổng số mọi thứ có trong bản đồ bộ nhớ quy trình. Điều này bao gồm dữ liệu (ví dụ, heap Java), nhưng cũng bao gồm tất cả các thư viện dùng chung và các tệp ánh xạ bộ nhớ được sử dụng bởi chương trình. Trên Linux, bạn có thể sử dụng lệnh pmap để xem tất cả mọi thứ được ánh xạ vào không gian xử lý (từ đây trở đi tôi sẽ chỉ đề cập đến Linux, vì đó là những gì tôi sử dụng; tôi chắc chắn có các công cụ tương đương cho Các cửa sổ). Đây là một đoạn trích từ bản đồ bộ nhớ của chương trình "Hello World"; toàn bộ bản đồ bộ nhớ dài hơn 100 dòng và không có gì lạ khi có danh sách hàng nghìn dòng.
0000000040000000 36K rx-- /usr/local/java/jdk-1.6-x64/bin/java
0000000040108000 8K rwx-- /usr/local/java/jdk-1.6-x64/bin/java
0000000040eba000 676K rwx-- [anon]
00000006fae00000 21248K rwx-- [anon]
00000006fc2c0000 62720K rwx-- [anon]
0000000700000000 699072K rwx-- [anon]
000000072aab0000 2097152K rwx-- [anon]
00000007aaab0000 349504K rwx-- [anon]
00000007c0000000 1048576K rwx-- [anon]
...
00007fa1ed00d000 1652K r-xs- /usr/local/java/jdk-1.6-x64/jre/lib/rt.jar
...
00007fa1ed1d3000 1024K rwx-- [anon]
00007fa1ed2d3000 4K ----- [anon]
00007fa1ed2d4000 1024K rwx-- [anon]
00007fa1ed3d4000 4K ----- [anon]
...
00007fa1f20d3000 164K rx-- /usr/local/java/jdk-1.6-x64/jre/lib/amd64/libjava.so
00007fa1f20fc000 1020K ----- /usr/local/java/jdk-1.6-x64/jre/lib/amd64/libjava.so
00007fa1f21fb000 28K rwx-- /usr/local/java/jdk-1.6-x64/jre/lib/amd64/libjava.so
...
00007fa1f34aa000 1576K rx-- /lib/x86_64-linux-gnu/libc-2.13.so
00007fa1f3634000 2044K ----- /lib/x86_64-linux-gnu/libc-2.13.so
00007fa1f3833000 16K rx-- /lib/x86_64-linux-gnu/libc-2.13.so
00007fa1f3837000 4K rwx-- /lib/x86_64-linux-gnu/libc-2.13.so
...
Giải thích nhanh về định dạng: mỗi hàng bắt đầu bằng địa chỉ bộ nhớ ảo của phân khúc. Tiếp theo là kích thước phân khúc, quyền và nguồn của phân khúc. Mục cuối cùng này là một tệp hoặc "anon", biểu thị một khối bộ nhớ được phân bổ thông qua mmap .
Bắt đầu từ đầu, chúng tôi có
- Trình tải JVM (tức là chương trình được chạy khi bạn gõ
java
). Điều này rất nhỏ; tất cả những gì nó làm là tải trong các thư viện dùng chung nơi mã JVM thực được lưu trữ.
- Một loạt các khối anon giữ heap Java và dữ liệu nội bộ. Đây là Sun JVM, do đó, heap được chia thành nhiều thế hệ, mỗi thế hệ là khối bộ nhớ riêng. Lưu ý rằng JVM phân bổ không gian bộ nhớ ảo dựa trên
-Xmx
giá trị; điều này cho phép nó có một đống liền kề. Các -Xms
giá trị được sử dụng trong nội bộ để nói bao nhiêu của heap là "sử dụng" khi chương trình bắt đầu, và để thu gom rác thải kích hoạt như giới hạn đó được tiếp cận.
- Một JARfile được ánh xạ bộ nhớ, trong trường hợp này là tệp chứa "các lớp JDK." Khi bạn ánh xạ bộ nhớ JAR, bạn có thể truy cập các tệp trong đó rất hiệu quả (so với việc đọc nó từ đầu mỗi lần). Sun JVM sẽ ánh xạ bộ nhớ tất cả các JAR trên đường dẫn lớp; nếu mã ứng dụng của bạn cần truy cập JAR, bạn cũng có thể ánh xạ bộ nhớ.
- Dữ liệu trên mỗi luồng cho hai luồng. Khối 1M là ngăn xếp luồng. Tôi đã không có một lời giải thích tốt cho khối 4k, nhưng @ericsoe xác định đó là "khối bảo vệ": nó không có quyền đọc / ghi, do đó sẽ gây ra lỗi phân đoạn nếu được truy cập và JVM bắt và dịch nó đến a
StackOverFlowError
. Đối với một ứng dụng thực, bạn sẽ thấy hàng tá nếu không phải hàng trăm mục nhập này được lặp lại thông qua bản đồ bộ nhớ.
- Một trong những thư viện chia sẻ chứa mã JVM thực tế. Có một vài trong số này.
- Thư viện dùng chung cho thư viện chuẩn C. Đây chỉ là một trong nhiều thứ mà JVM tải không hoàn toàn là một phần của Java.
Các thư viện dùng chung đặc biệt thú vị: mỗi thư viện dùng chung có ít nhất hai phân đoạn: phân đoạn chỉ đọc chứa mã thư viện và phân đoạn đọc-ghi chứa dữ liệu trên toàn bộ quy trình cho thư viện (Tôi không biết gì về thư viện phân đoạn không có quyền là; tôi chỉ thấy nó trên x64 Linux). Phần chỉ đọc của thư viện có thể được chia sẻ giữa tất cả các quy trình sử dụng thư viện; ví dụ, libc
có 1,5M không gian bộ nhớ ảo có thể được chia sẻ.
Khi nào kích thước bộ nhớ ảo quan trọng?
Bản đồ bộ nhớ ảo chứa rất nhiều thứ. Một số trong số đó là chỉ đọc, một số được chia sẻ và một số được phân bổ nhưng không bao giờ chạm vào (ví dụ: gần như tất cả 4Gb heap trong ví dụ này). Nhưng hệ điều hành đủ thông minh để chỉ tải những gì nó cần, do đó kích thước bộ nhớ ảo phần lớn không liên quan.
Trong đó kích thước bộ nhớ ảo quan trọng là nếu bạn đang chạy trên hệ điều hành 32 bit, nơi bạn chỉ có thể phân bổ 2Gb (hoặc, trong một số trường hợp, 3Gb) không gian địa chỉ xử lý. Trong trường hợp đó, bạn đang xử lý một nguồn tài nguyên khan hiếm và có thể phải đánh đổi, chẳng hạn như giảm kích thước heap của bạn để ánh xạ bộ nhớ một tệp lớn hoặc tạo nhiều luồng.
Nhưng, do các máy 64 bit có mặt ở khắp mọi nơi, tôi không nghĩ sẽ còn lâu nữa trước khi Kích thước bộ nhớ ảo là một thống kê hoàn toàn không liên quan.
Khi nào Resident Set Size quan trọng?
Kích thước Resident Set là phần không gian bộ nhớ ảo thực sự nằm trong RAM. Nếu RSS của bạn phát triển thành một phần đáng kể trong tổng bộ nhớ vật lý của bạn, có lẽ đã đến lúc bắt đầu lo lắng. Nếu RSS của bạn phát triển để chiếm hết bộ nhớ vật lý của bạn và hệ thống của bạn bắt đầu hoán đổi, đã đến lúc bắt đầu lo lắng.
Nhưng RSS cũng gây hiểu nhầm, đặc biệt là trên một máy tải nhẹ. Hệ điều hành không tốn nhiều công sức để lấy lại các trang được sử dụng bởi một quy trình. Có rất ít lợi ích để đạt được bằng cách làm như vậy và khả năng xảy ra lỗi trang đắt tiền nếu quy trình chạm vào trang trong tương lai. Do đó, thống kê RSS có thể bao gồm nhiều trang không được sử dụng.
Dòng dưới cùng
Trừ khi bạn trao đổi, đừng quá lo lắng về những gì các số liệu thống kê bộ nhớ khác nhau đang nói với bạn. Với sự cảnh báo rằng một RSS ngày càng phát triển có thể chỉ ra một số loại rò rỉ bộ nhớ.
Với một chương trình Java, điều quan trọng hơn nhiều là phải chú ý đến những gì đang diễn ra trong đống. Tổng số lượng không gian tiêu thụ là quan trọng, và có một số bước bạn có thể thực hiện để giảm bớt điều đó. Quan trọng hơn là lượng thời gian bạn dành cho việc thu gom rác và phần nào của đống đang được thu thập.
Truy cập đĩa (tức là cơ sở dữ liệu) rất tốn kém và bộ nhớ thì rẻ. Nếu bạn có thể đổi cái này lấy cái kia, hãy làm như vậy.