Tại sao kích thước heap cố định trên JVM?


20

Bất cứ ai cũng có thể giải thích cho tôi tại sao các JVM (tôi đã không kiểm tra quá nhiều, nhưng tôi chưa bao giờ thấy một JVM nào làm như vậy) cần phải chạy trên một kích thước heap cố định? Tôi biết việc thực hiện trên một đống liền kề đơn giản dễ dàng hơn, nhưng Sun JVM hiện đã hơn một thập kỷ, vì vậy tôi hy vọng họ đã có thời gian để cải thiện điều này.

Cần xác định kích thước bộ nhớ tối đa của chương trình của bạn khi khởi động dường như là một việc cần làm trong những năm 1960, và sau đó có những tương tác xấu với quản lý bộ nhớ ảo OS (lấy ra dữ liệu đã hoán đổi, không thể xác định được quá trình bộ nhớ của Java là bao nhiêu thực sự sử dụng từ phía hệ điều hành, một lượng lớn dung lượng VM bị lãng phí (tôi biết, bạn không quan tâm đến các máy 48 bit ưa thích của mình ...)). Tôi cũng đoán rằng những nỗ lực đáng buồn khác nhau để xây dựng các hệ điều hành nhỏ bên trong JVM (máy chủ ứng dụng EE, OSGi) ít nhất là một phần để đổ lỗi cho tình huống này, bởi vì việc chạy nhiều quy trình Java trên một hệ thống luôn dẫn đến lãng phí tài nguyên vì bạn phải cung cấp cho mỗi người trong số họ bộ nhớ có thể phải sử dụng lúc cao điểm.

Đáng ngạc nhiên, Google đã không mang lại những cơn bão phẫn nộ về điều này mà tôi mong đợi, nhưng họ có thể đã bị chôn vùi dưới hàng triệu người tìm hiểu về kích thước đống cố định và thực tế là chấp nhận nó.


Thiết kế của các máy chủ ứng dụng EE sẽ có ý nghĩa hoàn hảo ngay cả khi không có "hoàn cảnh" này vì bản thân JVM cần một khoảng trống và chuyển đổi giữa các luồng rẻ hơn so với chuyển đổi giữa các quy trình - đó là một trong những điều làm cho Java trở nên lớn vào cuối những năm 90.
Michael Borgwardt

Đây là một câu nói hay và tôi tự hỏi bạn đã nghĩ đến nó nhiều như thế nào. Ví dụ: tương tác GC / hoán đổi sẽ thay đổi như thế nào nếu bạn không có giới hạn heap? Không gian VM bị lãng phí như thế nào? Bạn nhận được dung lượng 2 / 3Gb của mình cho dù bạn có sử dụng hay không và nếu bạn đẩy các giới hạn của không gian đó thì không quan trọng bạn có một đống cố định hay nổi. Đối với vấn đề đó, làm thế nào nhiều JVM lãng phí bất cứ thứ gì ngoài trao đổi (cần được cấu hình phù hợp cho mục đích của máy)?
kdgregory

2
Nó là một câu nói hay, nhưng nó được thông báo qua nhiều năm kinh nghiệm viết và vận hành một nền tảng dựa trên Java. Nếu bạn không thực hiện trao đổi (vì nó sẽ khiến hệ thống của bạn không phản hồi trong 20 phút cho đến khi quá trình chạy trốn hết dung lượng) và tắt bộ nhớ vì lý do ổn định (kẻ giết người OOM không giỏi trong việc chọn nạn nhân ), bạn quan tâm đến không gian VM và trái với những gì mọi người bên dưới đang ám chỉ, việc khởi chạy Java VM với -Xmx2048m sẽ phân bổ ngay 2GB bộ nhớ ảo (ít nhất là trong Sun JVM của tôi trên Linux) cho một chương trình có một biến.
themel

Câu hỏi tuyệt vời. Đang tự hỏi điều tương tự. Nhưng "sự thật" nào được trình bày ở đây trong q và câu trả lời là đúng?
Martin Ba

Đối với các phản ứng bạn đang tìm kiếm, chỉ cần đơn giản là theo dõi lỗi theo dõi mặt trời ... ví dụ ở đây , đâyđây . Đọc những điều đó và cảm nhận cơn thịnh nộ :)
Cơ bản

Câu trả lời:


23

Bạn sai rồi. Kích thước heap của JVM không cố định, chỉ giới hạn:

  • -Xmx đặt kích thước bộ nhớ heap tối đa
  • -Xms đặt kích thước bộ nhớ heap tối thiểu

Đặt giới hạn trên là cần thiết vì nhiều lý do. Đầu tiên, nó báo cho người thu gom rác khi nào sẽ hành động. Thứ hai, nó ngăn JVM làm tắc nghẽn toàn bộ máy bằng cách tiêu thụ quá nhiều bộ nhớ. Kích thước heap tối thiểu có thể hữu ích để dự trữ lượng bộ nhớ mà chương trình cần ít nhất, để ngăn chặn nó hết bộ nhớ (vì các quá trình khác tiêu thụ quá nhiều).


9
không có min là mặc định để tránh khởi động chậm khi cần phân bổ rất nhiều dẫn đến tăng heap lặp đi lặp lại, phân trang sẽ xử lý ram vật lý sắp hết
ratchet freak

@ratchetfreak đó là phỏng đoán thứ hai của tôi ;-)
user281377

@ user281377 Nếu đây là trường hợp, thì làm thế nào C # có thể chạy tốt mà không có kích thước bộ nhớ heap tối đa?
cmorse

cmorse: Tôi chỉ có thể đoán. Có thể Java được nhắm mục tiêu vào các máy chủ lớn, nơi nhiều ứng dụng chia sẻ tài nguyên và giới hạn được thực thi nghiêm ngặt là mong muốn, trong khi .net được tạo ra cho PC và các máy chủ nhỏ hơn, chuyên dụng hơn.
user281377

@ user281377: Các ứng dụng Java mà tôi đã sử dụng hết dung lượng heap thường xử lý rất kém, thường chỉ bị sập hoặc rất dễ hỏng sau đó. Và ASP.net chạy trên cả máy chủ lớn và nhỏ. Điều tôi thực sự không nhận được là tại sao theo mặc định, Java thực thi giới hạn này. Tôi rất thích nghe lý do đằng sau quyết định của họ ... Tôi chắc chắn rằng họ có lý do chính đáng.
cmorse

6

Tôi tưởng tượng câu trả lời có liên quan đến di sản của Java. Ban đầu nó được thiết kế như một ngôn ngữ được sử dụng cho các hệ thống nhúng, trong đó rõ ràng tài nguyên bị hạn chế và bạn không muốn các quy trình chỉ đơn giản là ngấu nghiến bất cứ thứ gì có sẵn. Nó cũng giúp quản trị hệ thống, vì nó giúp cung cấp tài nguyên trên máy chủ dễ dàng hơn nếu bạn có thể đặt giới hạn tài nguyên. Tôi thấy rằng các JVM mới nhất dường như sử dụng nhiều heap không liên tục, mặc dù tất nhiên tất cả đều xuất hiện dưới dạng một heap duy nhất cho mã của bạn.

.


1
+1 - vì (1) câu trả lời duy nhất thực sự giải quyết được câu hỏi và (2) có lý.
kdgregory

2

Bạn cần cung cấp cho GC một số cơ chế để báo cho nó biết khi nào chạy, hoặc chương trình của bạn sẽ lấp đầy toàn bộ không gian bộ nhớ ảo. Có nhiều cách khác nhau để kích hoạt GC: thời gian trôi qua, số lần phân bổ, số lần giao, có thể là những cách khác mà tôi không thể nghĩ ra ngay bây giờ. IMO không ai trong số đó tốt như chỉ đơn giản là thiết lập ranh giới bộ nhớ và chạy GC khi không gian được phân bổ chạm đến ranh giới đó.

Điều quan trọng là thiết lập các ranh giới chính xác . Tôi nhìn vào -ms"đây là bao nhiêu bộ nhớ ứng dụng của tôi cần" và -mxnhư "nó không bao giờ vượt quá số tiền này". Trong một triển khai sản xuất, hai nên được đóng nếu không bằng nhau, và chúng nên được dựa trên các yêu cầu đo lường thực tế.

Mối quan tâm của bạn về bộ nhớ ảo "lãng phí" bị đặt không đúng chỗ: đó là ảo, nó (gần như) miễn phí. Có, việc cung cấp một đống quá lớn có nghĩa là bạn không thể bắt đầu nhiều luồng hoặc tải nhiều tệp ánh xạ bộ nhớ. Nhưng đó là một phần của thiết kế ứng dụng: bạn có nguồn tài nguyên khan hiếm, bạn cần phân vùng chúng theo cách cho phép ứng dụng của bạn chạy. Trong một đống "kiểu C", sẽ mở rộng cho đến khi bạn đạt đến đỉnh bộ nhớ, vấn đề cơ bản là như vậy, bạn không cần phải suy nghĩ về nó cho đến khi bạn gặp rắc rối.

Điều duy nhất mà một đống lớn có thể "lãng phí" là không gian hoán đổi, bởi vì tất cả các phân khúc có thể ghi đều yêu cầu cam kết từ trao đổi. Nhưng đó là một phần của thiết kế hệ thống: nếu bạn muốn có nhiều JVM chạy trên cùng một hộp, hãy tăng trao đổi hoặc giảm phân bổ heap của chúng. Nếu họ bắt đầu đập, thì bạn đang cố gắng làm quá nhiều với hệ thống; mua thêm bộ nhớ (và nếu bạn vẫn đang chạy bộ xử lý 32 bit, hãy mua hộp mới).


1
Nhưng ngưỡng để chạy GC không cần phải làm gì với ngưỡng cố định mà tổng chương trình sử dụng bộ nhớ không thể vượt quá. . Xem bộ sưu tập rác thế hệ.
Ben

@Ben - vâng, bạn nói đúng. Và câu thứ hai của tôi chỉ ra rằng có những lựa chọn thay thế. Tuy nhiên, tôi không đồng ý rằng một đống kích thước cố định là cách sai để quản lý GC trong trường hợp chung. Một JVM được điều chỉnh đúng cách sử dụng kích thước heap "đúng"; theo kinh nghiệm của tôi, việc tạm dừng GC kéo dài xảy ra khi JVM chưa được điều chỉnh đúng. Và thường thì kích thước heap "đúng" nhỏ hơn nhiều so với bạn nghĩ.
kdgregory

-2

Như user281377 nói, bạn chỉ xác định giới hạn trên của bộ nhớ mà quá trình của bạn có thể tiêu thụ. Tất nhiên, bản thân ứng dụng sẽ chỉ lấy không gian mà nó cần.

Có nên tồn tại giới hạn trên mặc định hay không là một câu hỏi khác, với cả pro và contra.

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.