Sử dụng bộ nhớ ảo từ Java trong Linux, sử dụng quá nhiều bộ nhớ


259

Tôi gặp vấn đề với một ứng dụng Java chạy trên Linux.

Khi tôi khởi chạy ứng dụng, sử dụng kích thước heap tối đa mặc định (64 MB), tôi thấy bằng cách sử dụng ứng dụng ngọn mà 240 MB Bộ nhớ ảo được phân bổ cho ứng dụng. Điều này tạo ra một số vấn đề với một số phần mềm khác trên máy tính, tương đối hạn chế về tài nguyên.

Theo như tôi hiểu thì bộ nhớ ảo dành riêng sẽ không được sử dụng, bởi vì một khi chúng ta đạt đến giới hạn heap OutOfMemoryErrorthì sẽ bị ném. Tôi đã chạy cùng một ứng dụng dưới các cửa sổ và tôi thấy rằng kích thước Bộ nhớ ảo và kích thước Heap tương tự nhau.

Có cách nào để tôi có thể định cấu hình Bộ nhớ ảo được sử dụng cho một quy trình Java trong Linux không?

Chỉnh sửa 1 : Vấn đề không phải là Heap. Vấn đề là nếu tôi đặt Heap 128 MB, chẳng hạn, Linux vẫn phân bổ 210 MB Bộ nhớ ảo, điều này không cần thiết, bao giờ hết. **

Chỉnh sửa 2 : Sử dụng ulimit -vcho phép giới hạn dung lượng bộ nhớ ảo. Nếu kích thước được đặt dưới 204 MB, thì ứng dụng sẽ không chạy mặc dù không cần 204 MB, chỉ 64 MB. Vì vậy, tôi muốn hiểu tại sao Java đòi hỏi rất nhiều bộ nhớ ảo. Điều này có thể được thay đổi?

Chỉnh sửa 3 : Có một số ứng dụng khác đang chạy trong hệ thống, được nhúng. Và hệ thống có giới hạn bộ nhớ ảo (từ bình luận, chi tiết quan trọng).


Tại sao bạn quan tâm đến việc sử dụng bộ nhớ ảo? Nếu bạn thực sự muốn được quan tâm, hãy xem việc sử dụng bộ nhớ thường trú và đọc các lệnh sau: free, ps, top.
basszero

2
Có một số ứng dụng khác đang chạy trong hệ thống, được nhúng. Và hệ thống có giới hạn bộ nhớ ảo.
Mario Ortegón

ahhhh, ma quỷ có trong chi tiết
basszero

Bạn đang sử dụng triển khai Java nào. IIRC, Sun JRE miễn phí tiêu chuẩn (không phải OpenJDK) không được cấp phép sử dụng nhúng.
Tom Hawtin - tackline

Tôi nghĩ rằng tôi đã sử dụng sai phần "nhúng" ... nó bị giới hạn bộ nhớ và phần cứng được tùy chỉnh, nhưng nó vẫn là một máy tính tiêu chuẩn
Mario Ortegón

Câu trả lời:


630

Đâ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 -Xmxgiá trị; điều này cho phép nó có một đống liền kề. Các -Xmsgiá 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ụ, libccó 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.


9
Bạn nên tính đến việc các phần của bộ nhớ hiện đang bị tráo đổi bị thiếu trong thước đo RES. Vì vậy, bạn có thể có giá trị RES thấp nhưng chỉ vì ứng dụng không hoạt động và phần lớn heap đã được hoán đổi vào đĩa. Java thực hiện một công việc rất tệ để trao đổi: Trên mỗi GC đầy đủ, phần lớn heap được chuyển và sao chép, vì vậy nếu phần lớn heap của bạn được trao đổi, thì GC phải tải lại tất cả vào bộ nhớ chính.
jrudolph

1
Câu trả lời tuyệt vời kdgregory! Tôi đang chạy trong môi trường nhúng bằng CF không có không gian hoán đổi. Vì vậy, dựa trên câu trả lời của bạn, tất cả các giá trị VIRT, SWAP và nFLT của tôi là từ các tệp được ánh xạ bộ nhớ ... mà giờ đây có ý nghĩa để mew. Bạn có biết nếu giá trị SWAP đại diện cho các trang chưa được tải vào bộ nhớ hoặc các trang đã bị tráo đổi khỏi bộ nhớ hoặc cả hai? Làm thế nào chúng ta có thể có được một ý tưởng về khả năng đập (bản đồ liên tục sau đó trao đổi)?
Jeach

2
@Jeach - Tôi đã ngạc nhiên khi bất kỳ trao đổi nào được báo cáo, vì vậy đã khởi động "Linux du lịch" của tôi (một ổ đĩa ngón tay với Ubuntu 10.04 và không có trao đổi). Khi tôi bật cột "SWAP" ở trên cùng , tôi thấy Eclipse có 509m. Khi tôi nhìn nó với pmap , tổng không gian ảo là 650m. Vì vậy, tôi nghi ngờ rằng con số "SWAP" đại diện cho tất cả các trang trên đĩa, không chỉ những trang không có trong bộ nhớ.
kdgregory

2
Đối với câu hỏi thứ hai của bạn: nếu bạn liên tục đọc các trang từ thẻ flash, thời gian chờ IO của bạn (được hiển thị trong phần tóm tắt hàng đầu là "% wa") sẽ cao. Tuy nhiên, hãy cẩn thận rằng điều này sẽ cao cho bất kỳ hoạt động nào, đặc biệt là viết (giả sử rằng chương trình của bạn thực hiện bất kỳ).
kdgregory

1
> Khối 1M là ngăn xếp luồng; Tôi không biết những gì đi vào khối 4K. Khối 4K - được đánh dấu là không có quyền đọc và ghi - có khả năng là khối bảo vệ. Khi tràn ngăn xếp, khu vực này được truy cập, sẽ gây ra lỗi, khi đó JVM có thể xử lý bằng cách tạo Java StackOverflowException. Đây là cách rẻ hơn so với việc kiểm tra con trỏ ngăn xếp tại mỗi lệnh gọi phương thức. Các khu vực bảo vệ không có quyền được thiết lập cũng có thể được nhìn thấy được sử dụng trong các bối cảnh khác.
eriksoe

38

Có một vấn đề đã biết với Java và glibc> = 2.10 (bao gồm Ubuntu> = 10.04, RHEL> = 6).

Cách chữa là đặt env này. Biến đổi:

export MALLOC_ARENA_MAX=4

Nếu bạn đang chạy Tomcat, bạn có thể thêm phần này vào TOMCAT_HOME/bin/setenv.shtệp.

Đối với Docker, hãy thêm cái này vào Dockerfile

ENV MALLOC_ARENA_MAX=4

Có một bài viết của IBM về việc thiết lập MALLOC_ARENA_MAX https://www.ibm.com/developerworks/community/bloss/kevgrig/entry/linux_glibc_2_10_rhel_6_malloc_may_show_excessive_virtual_memory_us

Bài đăng trên blog này nói

bộ nhớ thường trú đã được biết là leo theo cách tương tự như rò rỉ bộ nhớ hoặc phân mảnh bộ nhớ.

Ngoài ra còn có một lỗi JDK mở JDK -8193521 "glibc làm lãng phí bộ nhớ với cấu hình mặc định"

tìm kiếm MALLOC_ARENA_MAX trên Google hoặc SO để có thêm tài liệu tham khảo.

Bạn cũng có thể muốn điều chỉnh các tùy chọn malloc khác để tối ưu hóa cho phân mảnh bộ nhớ được phân bổ thấp:

# tune glibc memory allocation, optimize for low fragmentation
# limit the number of arenas
export MALLOC_ARENA_MAX=2
# disable dynamic mmap threshold, see M_MMAP_THRESHOLD in "man mallopt"
export MALLOC_MMAP_THRESHOLD_=131072
export MALLOC_TRIM_THRESHOLD_=131072
export MALLOC_TOP_PAD_=131072
export MALLOC_MMAP_MAX_=65536

Câu trả lời này thực sự đã giúp tôi trên Máy chủ Ubuntu 64 bit với máy chủ TomEE có một litte để "tiêu thụ mem". Liên kết đến bài viết của IBM thực sự là một lời giải thích sâu sắc. Cảm ơn một lần nữa cho gợi ý tốt này!
MWiesner

1
JVM có thể rò rỉ bộ nhớ riêng dẫn đến các triệu chứng tương tự. Xem stackoverflow.com/a/35610063/166062 . Các trường hợp GZIPInputStream và GZIPOutputStream không được tiết lộ cũng có thể là một nguồn rò rỉ.
Lari Hotari

3
Có một lỗi JVM trong Java 8, dẫn đến tăng trưởng bộ nhớ riêng không bị ràng buộc: bug.java.com/bugdatabase/view_orms.do?orms_id=JDK-8164293 - Nếu điều này ảnh hưởng đến bạn, việc sử dụng MALLOC_ARENA_MAXcó thể làm chậm sự tăng trưởng bộ nhớ của bạn, nhưng không giải quyết vấn đề hoàn toàn
outofcoffee

@LariHotari thực sự đánh giá cao nỗ lực của bạn trong việc chỉ ra phiên bản glibc và redhat
Sam

2
Java 8u131 chứa lỗi sửa lỗi backported cho lỗi JVM liên quan JDK-8164293 bug.openjdk.java.net/browse/JDK-8178124 .
Lari Hotari

9

Dung lượng bộ nhớ được phân bổ cho quy trình Java tương đương với những gì tôi mong đợi. Tôi đã gặp vấn đề tương tự khi chạy Java trên các hệ thống giới hạn nhúng / bộ nhớ. Chạy bất kỳ ứng dụng nào có giới hạn VM tùy ý hoặc trên các hệ thống không có đủ số lượng trao đổi có xu hướng bị hỏng. Dường như đó là bản chất của nhiều ứng dụng hiện đại không được thiết kế để sử dụng trên các hệ thống giới hạn tài nguyên.

Bạn có thêm một vài tùy chọn mà bạn có thể thử và giới hạn dung lượng bộ nhớ của JVM. Điều này có thể làm giảm dung lượng bộ nhớ ảo:

-XX: ReservedCodeCacheSize = 32m Kích thước bộ đệm mã dự trữ (tính bằng byte) - kích thước bộ đệm mã tối đa. [Solaris 64-bit, amd64 và -server x86: 48m; trong 1.5.0_06 trở về trước, Solaris 64 bit và 64: 1024m.]

-XX: MaxPermSize = 64m Kích thước của thế hệ vĩnh viễn. [5.0 và mới hơn: Máy ảo 64 bit được chia tỷ lệ lớn hơn 30%; 1,4 amd64: 96m; 1.3.1 -client: 32m.]

Ngoài ra, bạn cũng nên đặt -Xmx (kích thước heap tối đa) của mình thành một giá trị càng gần càng tốt với mức sử dụng bộ nhớ tối đa thực tế của ứng dụng của bạn. Tôi tin rằng hành vi mặc định của JVM vẫn là tăng gấp đôi kích thước heap mỗi khi nó mở rộng nó lên tối đa. Nếu bạn bắt đầu với heap 32M và ứng dụng của bạn đạt đỉnh 65M, thì heap cuối cùng sẽ tăng 32M -> 64M -> 128M.

Bạn cũng có thể thử điều này để làm cho VM bớt tích cực hơn trong việc tăng số lượng lớn:

-XX: MinHeapFreeRatio = 40 Tỷ lệ phần trăm heap miễn phí tối thiểu sau GC để tránh mở rộng.

Ngoài ra, từ những gì tôi nhớ lại từ thử nghiệm này vài năm trước, số lượng thư viện gốc được tải có tác động rất lớn đến dấu chân tối thiểu. Đang tải java.net.Socket đã thêm hơn 15 triệu nếu tôi nhớ chính xác (và có lẽ tôi không biết).


7

Sun JVM đòi hỏi rất nhiều bộ nhớ cho HotSpot và nó ánh xạ trong các thư viện thời gian chạy trong bộ nhớ dùng chung.

Nếu bộ nhớ là một vấn đề, hãy cân nhắc sử dụng một JVM khác phù hợp để nhúng. IBM có j9 và có "jamvm" mã nguồn mở sử dụng các thư viện đường dẫn lớp GNU. Ngoài ra, Sun có JVM Squeak chạy trên SunSPOTS nên có những lựa chọn thay thế.


Nó có phải là một lựa chọn để vô hiệu hóa điểm nóng?
Mario Ortegón

Có lẽ. Kiểm tra các tùy chọn dòng lệnh cho JVM mà bạn sử dụng.
Thorbjørn Ravn Andersen

3

Chỉ là một suy nghĩ, nhưng bạn có thể kiểm tra ảnh hưởng của một ulimit -vtùy chọn .

Đó không phải là một giải pháp thực tế vì nó sẽ giới hạn không gian địa chỉ có sẵn cho tất cả quá trình, nhưng điều đó sẽ cho phép bạn kiểm tra hành vi của ứng dụng của mình với bộ nhớ ảo hạn chế.


Đó chính xác là vấn đề của tôi. My Heap được đặt thành 64M, nhưng linux dự trữ 204MB. Nếu tôi đặt ulimit dưới 204, ứng dụng hoàn toàn không chạy.
Mario Ortegón

Thú vị: thiết lập ulimit có thể có tác dụng phụ ngoài ý muốn cho các quy trình khác, giải thích lý do tại sao ứng dụng không thể chạy.
VonC

Vấn đề dường như là Java yêu cầu phải dự trữ lượng bộ nhớ ảo lớn hơn mặc dù nó sẽ không sử dụng nó. Trong các cửa sổ, bộ nhớ ảo được sử dụng và cài đặt Xmx khá gần hơn.
Mario Ortegón

Bạn đã thử nó với một JVM của JRockit chưa?
VonC

Vì cấp phát bộ nhớ của JVM là tổng của Phân bổ vùng heap và Kích thước Perm (lần đầu tiên có thể được sửa bằng các tùy chọn -Xms và -Xmx), bạn đã thử một số cài đặt với -XX: PermSize và -XX: MaxPermSize (mặc định từ 32MB đến 64MB tùy thuộc vào phiên bản JVM)?
VonC

3

Một cách để giảm số lượng heap của một hệ thống có tài nguyên hạn chế có thể là chơi xung quanh với biến -XX: MaxHeapFreeRatio. Giá trị này thường được đặt thành 70 và là tỷ lệ phần trăm tối đa của heap miễn phí trước khi GC thu nhỏ nó. Đặt nó thành một giá trị thấp hơn, và bạn sẽ thấy trong ví dụ trình hồ sơ jvisualvm rằng một con heap nhỏ hơn thường được sử dụng cho chương trình của bạn.

EDIT: Để đặt các giá trị nhỏ cho -XX: MaxHeapFreeRatio, bạn cũng phải đặt -XX: MinHeapFreeRatio Eg

java -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=25 HelloWorld

EDIT2: Đã thêm một ví dụ cho một ứng dụng thực sự khởi động và thực hiện cùng một tác vụ, một với các tham số mặc định và một với 10 và 25 làm tham số. Tôi đã không nhận thấy bất kỳ sự khác biệt tốc độ thực sự, mặc dù lý thuyết java nên sử dụng nhiều thời gian hơn để tăng heap trong ví dụ sau.

Thông số mặc định

Cuối cùng, heap tối đa là 905, heap được sử dụng là 378

Tối thiểu 10, tối đa 25

Cuối cùng, heap tối đa là 722, heap được sử dụng là 378

Điều này thực sự có một số không chính xác, vì ứng dụng của chúng tôi chạy trên một máy chủ để bàn từ xa và nhiều người dùng có thể chạy nó cùng một lúc.


1

Java 1.4 của Sun có các đối số sau đây để kiểm soát kích thước bộ nhớ:

-Xmsn Chỉ định kích thước ban đầu, tính bằng byte, của nhóm cấp phát bộ nhớ. Giá trị này phải là bội số của 1024 lớn hơn 1MB. Nối chữ k hoặc K để chỉ kilobyte, hoặc m hoặc M để chỉ megabyte. Giá trị mặc định là 2MB. Ví dụ:

           -Xms6291456
           -Xms6144k
           -Xms6m

-Xmxn Chỉ định kích thước tối đa, tính bằng byte, của nhóm cấp phát bộ nhớ. Giá trị này phải là bội số của 1024 lớn hơn 2MB. Nối chữ k hoặc K để chỉ kilobyte, hoặc m hoặc M để chỉ megabyte. Giá trị mặc định là 64MB. Ví dụ:

           -Xmx83886080
           -Xmx81920k
           -Xmx80m

http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/java.html

Java 5 và 6 có thêm một số. Xem http://java.sun.com/javase/technologists/hotspot/vmoptions.jsp


1
Vấn đề tôi gặp phải không phải ở Kích thước Heap, mà là với Bộ nhớ ảo được gán bởi Linux
Mario Ortegón

Đọc lời giải thích của kdgregory. Giảm kích thước heap, "Kích thước mới" và các tham số cấu hình khác sẽ làm giảm dung lượng bộ nhớ THỰC mà jvm mất.
Paul Tomblin

Anh ta có thể có một vấn đề chính đáng. Một số ứng dụng (như tôi đã viết) mmap tệp 1 GB và một số hệ thống chỉ có 2 GB bộ nhớ ảo, một số trong đó chứa đầy thư viện dùng chung. Và nếu đây là vấn đề, anh ấy chắc chắn nên vô hiệu hóa ngẫu nhiên DSO. Có một tùy chọn trong / Proc.
Zan Lynx

0

Không, bạn không thể định cấu hình dung lượng bộ nhớ cần thiết cho VM. Tuy nhiên, lưu ý rằng đây là bộ nhớ ảo, không phải là cư dân, vì vậy nó chỉ ở đó mà không gây hại nếu không thực sự được sử dụng.

Về mặt bản chất, bạn có thể thử một số JVM khác sau đó là Sun one, với dung lượng bộ nhớ nhỏ hơn, nhưng tôi không thể khuyên ở đây.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.