Máy ảo Java - Điều chỉnh GC
Trong chương trước, chúng ta đã tìm hiểu về các Gcs Thế hệ khác nhau. Trong chương này, chúng ta sẽ thảo luận về cách điều chỉnh GC.
Kích thước kinh ngạc
Kích thước heap là một yếu tố quan trọng trong hiệu suất của các ứng dụng Java của chúng tôi. Nếu nó quá nhỏ, thì nó sẽ bị lấp đầy thường xuyên và do đó, GC sẽ phải thường xuyên thu thập. Mặt khác, nếu chúng ta chỉ tăng kích thước của đống, mặc dù nó cần được thu thập ít thường xuyên hơn, thì thời gian tạm dừng sẽ tăng lên.
Hơn nữa, việc tăng kích thước heap sẽ bị phạt nặng đối với hệ điều hành cơ bản. Sử dụng phân trang, hệ điều hành làm cho các chương trình ứng dụng của chúng tôi thấy nhiều bộ nhớ hơn so với thực tế. Hệ điều hành quản lý điều này bằng cách sử dụng một số không gian hoán đổi trên đĩa, sao chép các phần không hoạt động của chương trình vào đó. Khi cần những phần đó, hệ điều hành sẽ sao chép chúng trở lại từ đĩa vào bộ nhớ.
Giả sử rằng một máy có 8G bộ nhớ và JVM thấy 16G bộ nhớ ảo, thì JVM sẽ không biết rằng trên thực tế chỉ có 8G trên hệ thống. Nó sẽ chỉ yêu cầu 16G từ hệ điều hành và khi nhận được bộ nhớ đó, nó sẽ tiếp tục sử dụng. Hệ điều hành sẽ phải trao đổi rất nhiều dữ liệu vào và ra, và đây là một hình phạt hiệu suất rất lớn trên hệ thống.
Và sau đó là các tạm dừng sẽ xảy ra trong GC đầy đủ của bộ nhớ ảo đó. Vì GC sẽ hoạt động trên toàn bộ heap để thu thập và nén, nó sẽ phải đợi rất nhiều để bộ nhớ ảo được hoán đổi ra khỏi đĩa. Trong trường hợp có bộ thu đồng thời, các luồng nền sẽ phải đợi rất nhiều để dữ liệu được sao chép từ không gian hoán đổi vào bộ nhớ.
Vì vậy, ở đây câu hỏi làm thế nào chúng ta nên quyết định về kích thước đống tối ưu. Quy tắc đầu tiên là không bao giờ yêu cầu hệ điều hành nhiều bộ nhớ hơn thực tế. Điều này sẽ hoàn toàn ngăn chặn vấn đề hoán đổi thường xuyên. Nếu máy có nhiều JVM được cài đặt và đang chạy, thì tổng yêu cầu bộ nhớ của tất cả chúng cộng lại sẽ nhỏ hơn RAM thực tế có trong hệ thống.
Bạn có thể kiểm soát kích thước yêu cầu bộ nhớ của JVM bằng cách sử dụng hai cờ -
-XmsN - Kiểm soát bộ nhớ ban đầu được yêu cầu.
-XmxN - Kiểm soát bộ nhớ tối đa có thể được yêu cầu.
Giá trị mặc định của cả hai cờ này phụ thuộc vào hệ điều hành cơ bản. Ví dụ: đối với 64b JVM chạy trên MacOS, -XmsN = 64M và -XmxN = tối thiểu 1G hoặc 1/4 tổng bộ nhớ vật lý.
Lưu ý rằng JVM có thể tự động điều chỉnh giữa hai giá trị. Ví dụ: nếu nó nhận thấy rằng có quá nhiều GC đang xảy ra, nó sẽ tiếp tục tăng kích thước bộ nhớ miễn là nó dưới -XmxN và các mục tiêu hiệu suất mong muốn được đáp ứng.
Nếu bạn biết chính xác dung lượng bộ nhớ mà ứng dụng của bạn cần, thì bạn có thể đặt -XmsN = -XmxN. Trong trường hợp này, JVM không cần phải tìm ra giá trị “tối ưu” của đống, và do đó, quy trình GC trở nên hiệu quả hơn một chút.
Kích thước thế hệ
Bạn có thể quyết định về việc bạn muốn phân bổ bao nhiêu đống cho YG và bạn muốn phân bổ bao nhiêu cho OG. Cả hai giá trị này đều ảnh hưởng đến hiệu suất của các ứng dụng của chúng tôi theo cách sau.
Nếu quy mô của YG là rất lớn, thì nó sẽ được thu thập ít thường xuyên hơn. Điều này sẽ dẫn đến số lượng đối tượng được thăng cấp lên OG ít hơn. Mặt khác, nếu bạn tăng kích thước của OG quá nhiều, thì việc thu thập và thu gọn nó sẽ mất quá nhiều thời gian và điều này sẽ dẫn đến việc dừng STW lâu. Do đó, người dùng phải tìm sự cân bằng giữa hai giá trị này.
Dưới đây là các cờ mà bạn có thể sử dụng để đặt các giá trị này:
-XX:NewRatio=N: Tỷ lệ YG so với OG (giá trị mặc định = 2)
-XX:NewSize=N: Quy mô ban đầu của YG
-XX:MaxNewSize=N: Kích thước tối đa của YG
-XmnN: Đặt NewSize và MaxNewSize thành cùng một giá trị bằng cách sử dụng cờ này
Quy mô ban đầu của YG được xác định bởi giá trị của NewRatio theo công thức đã cho:
(total heap size) / (newRatio + 1)
Vì giá trị ban đầu của newRatio là 2, nên công thức trên cho giá trị ban đầu của YG là 1/3 tổng kích thước heap. Bạn luôn có thể ghi đè giá trị này bằng cách chỉ định rõ ràng kích thước của YG bằng cờ NewSize. Cờ này không có bất kỳ giá trị mặc định nào, và nếu nó không được đặt rõ ràng, kích thước của YG sẽ tiếp tục được tính bằng công thức trên.
Permagen và Metaspace
Permagen và metaspace là các vùng heap nơi JVM lưu giữ siêu dữ liệu của các lớp. Không gian được gọi là 'permagen' trong Java 7 và trong Java 8, nó được gọi là 'metaspace'. Thông tin này được sử dụng bởi trình biên dịch và thời gian chạy.
Bạn có thể kiểm soát kích thước của permagen bằng các cờ sau: -XX: PermSize=N và -XX:MaxPermSize=N. Kích thước của Metaspace có thể được kiểm soát bằng cách sử dụng:-XX:Metaspace- Size=N và -XX:MaxMetaspaceSize=N.
Có một số khác biệt về cách permagen và metaspace được quản lý khi các giá trị cờ không được đặt. Theo mặc định, cả hai đều có kích thước ban đầu mặc định. Nhưng trong khi metaspace có thể chiếm nhiều heap khi cần thiết, thì permagen có thể chiếm không nhiều hơn các giá trị ban đầu mặc định. Ví dụ: 64b JVM có 82M không gian heap là kích thước tối đa.
Lưu ý rằng vì siêu không gian có thể chiếm lượng bộ nhớ không giới hạn trừ khi được chỉ định không, nên có thể xảy ra lỗi hết bộ nhớ. GC đầy đủ diễn ra bất cứ khi nào các vùng này được thay đổi kích thước. Do đó, trong quá trình khởi động, nếu có nhiều lớp đang được tải, metaspace có thể tiếp tục thay đổi kích thước dẫn đến GC đầy đủ mọi lúc. Do đó, các ứng dụng lớn sẽ mất rất nhiều thời gian để khởi động trong trường hợp kích thước metaspace ban đầu quá thấp. Bạn nên tăng kích thước ban đầu vì nó làm giảm thời gian khởi động.
Mặc dù permagen và metaspace giữ siêu dữ liệu của lớp, nó không phải là vĩnh viễn và không gian được GC thu hồi, như trong trường hợp các đối tượng. Điều này thường xảy ra đối với các ứng dụng máy chủ. Bất cứ khi nào bạn thực hiện một triển khai mới cho máy chủ, siêu dữ liệu cũ phải được dọn dẹp vì các bộ tải lớp mới bây giờ sẽ cần dung lượng. Không gian này được giải phóng bởi GC.