Điều gì tiêu thụ bộ nhớ trong quá trình java?


20

chúng tôi đang cố gắng điều tra việc sử dụng bộ nhớ của quá trình java dưới tải vừa phải.

  PID   USER    PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
  12663 test    20   0 8378m 6.0g 4492 S   43  8.4 162:29.95 java

Như bạn có thể thấy chúng tôi có bộ nhớ thường trú tại 6Gb. Bây giờ phần thú vị là đây: quá trình được thực hiện với các thông số này:

  • -Xmx2048m
  • -Xms2048m
  • -XX: Kích thước mới = 512m
  • -XX: MaxDirectMemorySize = 256m
  • ... một số người khác cho GC và công cụ

Nhìn vào các cài đặt này và sử dụng bộ nhớ thực tế, chúng tôi tình cờ thấy được sự khác biệt của những gì chúng tôi mong đợi quá trình này sẽ được sử dụng và những gì nó thực sự sử dụng.

Thông thường các vấn đề về bộ nhớ của chúng ta được giải quyết bằng cách phân tích đống heap nhưng trong trường hợp này bộ nhớ của chúng ta được sử dụng ở đâu đó bên ngoài heap.

Câu hỏi: Điều gì sẽ là các bước để thử và tìm lý do của việc sử dụng bộ nhớ cao như vậy? Những công cụ nào có thể giúp chúng ta xác định những gì sử dụng bộ nhớ trong quá trình đó?

CHỈNH SỬA 0

Có vẻ như đây không phải là vấn đề liên quan đến đống vì chúng ta vẫn còn khá nhiều khoảng trống ở đó:

jmap -heap 12663

kết quả trong (chỉnh sửa để tiết kiệm không gian)

Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize      = 2147483648 (2048.0MB)
NewSize          = 536870912 (512.0MB)
MaxNewSize       = 536870912 (512.0MB)
OldSize          = 1610612736 (1536.0MB)
NewRatio         = 7
SurvivorRatio    = 8
PermSize         = 21757952 (20.75MB)
MaxPermSize      = 85983232 (82.0MB)

New Generation: 45.7% used
Eden Space: 46.3% used
From Space: 41.4% used
To Space: 0.0% used
concurrent mark-sweep generation: 63.7% used
Perm Generation: 82.5% used

CHỈNH SỬA 1

sử dụng pmap chúng ta có thể thấy rằng có khá nhiều phân bổ 64Mb:

pmap -x 12663 | grep rwx | sort -n -k3 | less

kết quả trong:

... a lot more of these 64Mb chunks
00007f32b8000000       0   65508   65508 rwx--    [ anon ] <- what are these?
00007f32ac000000       0   65512   65512 rwx--    [ anon ]
00007f3268000000       0   65516   65516 rwx--    [ anon ]
00007f3324000000       0   65516   65516 rwx--    [ anon ]
00007f32c0000000       0   65520   65520 rwx--    [ anon ]
00007f3314000000       0   65528   65528 rwx--    [ anon ] 
00000000401cf000       0  241904  240980 rwx--    [ anon ] <- Direct memory ?
000000077ae00000       0 2139688 2139048 rwx--    [ anon ] <- Heap ?

Vậy làm thế nào để tìm ra những khối 64Mb đó là gì? Sử dụng chúng là gì? Những loại dữ liệu trong họ?

Cảm ơn


2
Tôi đã nhận được chính xác cùng một vấn đề ... đây là câu hỏi của tôi. stackoverflow.com/questions/18734389/ cấp Bạn có giải pháp nào về vấn đề này không?
DeepNightTwo

Câu trả lời:


21

Vấn đề có thể liên quan đến vấn đề glibc này .

Về cơ bản, khi bạn có nhiều luồng cấp phát bộ nhớ, glibc sẽ tăng quy mô số lượng đấu trường có sẵn để phân bổ để tránh tranh chấp khóa. Một đấu trường rộng 64Mb. Giới hạn trên là tạo ra 8 lần số của các đấu trường lõi. Đấu trường sẽ được tạo theo yêu cầu khi một luồng truy cập vào một đấu trường đã bị khóa để nó phát triển theo thời gian.

Trong Java nơi bạn rắc các luồng, điều này có thể nhanh chóng dẫn đến rất nhiều đấu trường được tạo ra. Và có sự phân bổ lan rộng khắp các đấu trường này. Ban đầu, mỗi đấu trường 64Mb chỉ được ánh xạ bộ nhớ không được cấp phép, nhưng khi bạn phân bổ, bạn bắt đầu sử dụng bộ nhớ thực cho chúng.

Pmap của bạn có thể có danh sách tương tự như dưới đây. Lưu ý cách 324K + 65212K = 65536K, 560K + 64976K == 65536K, 620K + 64916K == 65536K. Đó là, họ tổng hợp lên đến 64Mb.

00007f4394000000 324K rw --- [anon]
00007f4394051000 65212K ----- [anon]
00007f4398000000 560K rw --- [anon]
00007f439808c000 64976K ----- [anon]
00007f439c000000 620K rw --- [anon]
00007f439c09b000 64916K ----- [anon]

Đối với cách giải quyết : Lỗi đề cập đến một số tham số môi trường bạn có thể đặt để giới hạn số lượng đấu trường, nhưng bạn cần một phiên bản glibc đủ cao.


5
đặt Xmx và Xms thành cùng một giá trị, cộng với đặt biến môi trường "export MALLOC_ARENA_MAX = 4" trong tập lệnh sh khởi động dịch vụ web của chúng tôi trong trường hợp của chúng tôi. Trước đó, chúng tôi đã trải nghiệm khởi động lại dịch vụ web do OOM Killer cứ sau 2 đến 8 giờ. Phiên bản GLIBC trong Ubuntu 14.04 là 2.19 là tốt, vì nó cần phải = = 2.16 để cài đặt MALLOC_ARENA_MAX hoạt động
Kluyg

Câu trả lời này và nhận xét trên là cứu cánh cho tôi. Trong trường hợp của tôi, MALLOC_ARENA_MAX = 1 là cần thiết và hiệu quả.
John Bachir

3

Làm thế nào về thăm dò Lamdba ? Trong số những thứ khác, nó có thể hiển thị cho bạn các sự cố sử dụng bộ nhớ tương tự như ảnh chụp màn hình bên dưới:

Chế độ xem sử dụng bộ nhớ thăm dò Lambda

Đôi khi pmap -x your_java_pidcũng có thể hữu ích.


Cảm ơn câu trả lời của bạn. Nếu tôi hiểu chính xác thì Lambda thăm dò là dành cho Apache Tomcat? Mà chúng tôi không sử dụng ... Về pmap, tôi sẽ thêm thông tin vào bài đăng hàng đầu
Konstantin S.

2

JProfiler có thể là thứ bạn tìm kiếm nhưng nó không miễn phí. Một công cụ tốt và miễn phí khác để điều tra việc sử dụng bộ nhớ của quá trình java là Java VisualVM có sẵn dưới dạng công cụ JDK trong các bản phân phối JDK của Oracle / Sun. Cá nhân tôi khuyên bạn nên tiếp cận toàn diện hơn cho vấn đề (nghĩa là giám sát các đĩa JDK + OS +, v.v.) - việc sử dụng một số hệ thống giám sát mạng - Nagios, Verax NMS hoặc OpenNMS.


2
Rò rỉ của anh ấy là quá lớn, vì vậy JProfiler sẽ không thực sự giúp đỡ ở đây
Asaf Mesika

2

Vấn đề nằm ngoài đống, vì vậy ứng cử viên tốt nhất là:

JNI leak  
Allocation of direct memory buffer

Do thực tế bạn đã giới hạn kích thước bộ đệm trực tiếp, theo tôi, ứng cử viên tốt nhất là rò rỉ JNI.


1

Có một công cụ tiện dụng để xem phân bổ bộ nhớ heap có trong JDK được gọi là jmap, trên đầu trang này bạn cũng có ngăn xếp vv (Xss). Chạy hai lệnh jmap này để có thêm thông tin về việc sử dụng bộ nhớ:

jmap -heap <PID>
jmap -permstat <PID>

Để có được nhiều thông tin hơn nữa, bạn có thể kết nối với quy trình bằng jconsole (cũng được bao gồm trong JDK). Tuy nhiên, Jconsole yêu cầu JMX được cấu hình trong ứng dụng.


Cảm ơn câu trả lời của bạn nhưng vấn đề dường như ở đâu đó ngoài đống. Tôi sẽ cập nhật bài đăng hàng đầu để phản ánh một số thông tin từ jmap
Konstantin S.

Quá trình có bao nhiêu chủ đề? Kích thước ngăn xếp là 2MB mặc định trên hầu hết các nền tảng, nhân với số lượng luồng. Tôi sẽ ngạc nhiên nếu nó chiếm tất cả bộ nhớ "mất tích" nhưng có thể là một số bộ nhớ.
HampusLi

Khoảng 300 chủ đề, lấy thông tin từ pmap, chúng tôi có kích thước ngăn xếp là 1Mb. Và từ cùng một đầu ra pmap, có vẻ như bất kỳ ngăn xếp nào cũng sử dụng hơn 100Kb
Konstantin S.

0

Sử dụng JVisualVM. Nó có nhiều khung nhìn khác nhau sẽ cho bạn biết bao nhiêu bộ nhớ heap đang được sử dụng, PermGen và vv.

Để trả lời câu hỏi của bạn. Java xử lý bộ nhớ hoàn toàn khác với những gì bạn có thể mong đợi.

Khi bạn đặt tham số -Xms và -Xmx, bạn đang nói với JVM rằng nó sẽ phân bổ bao nhiêu bộ nhớ trong heap để bắt đầu và cũng nên phân bổ tối đa bao nhiêu.

Nếu bạn có một ứng dụng java sử dụng tổng cộng 1m bộ nhớ nhưng được truyền trong -Xms256m -Xmx2g thì JVM sẽ tự khởi tạo với 256m bộ nhớ được sử dụng. Nó sẽ không sử dụng ít hơn thế. Không quan trọng là ứng dụng của bạn chỉ sử dụng bộ nhớ 1m.

Thứ hai. Trong trường hợp trên, nếu ứng dụng của bạn tại một số điểm sử dụng hơn 256m bộ nhớ, JVM sẽ phân bổ càng nhiều bộ nhớ càng cần thiết để phục vụ yêu cầu. Tuy nhiên, nó sẽ không giảm kích thước heap trở về giá trị tối thiểu. Ít nhất, không phải trong hầu hết các trường hợp.

Trong trường hợp của bạn, vì bạn đang đặt bộ nhớ tối thiểu và tối đa ở mức 2g, JVM sẽ phân bổ 2g khi bắt đầu và duy trì nó.

Quản lý bộ nhớ Java khá phức tạp và việc điều chỉnh sử dụng bộ nhớ có thể là một nhiệm vụ trong chính nó. Tuy nhiên, có rất nhiều nguồn lực có thể giúp đỡ.

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.