sách gpt4 ăn đã đi

Phân tích chuyên sâu | [Chuỗi sâu JVM] [Chuỗi nghiên cứu HotSpotVM] Phân tích các bẫy và cạm bẫy khác nhau của "tham số tiêu chuẩn" của điều chỉnh JVM (khắc phục điểm mù và điểm nhầm lẫn) "1"

In lại Tác giả: Tôi là chú chim nhỏ Thời gian cập nhật: 2022-12-16 14:31:31 34 4
mua khóa gpt4 giày nike

[Câu hỏi dễ mắc lỗi] Sự khác biệt giữa GC chính và GC đầy đủ là gì? Điều kiện kích hoạt thì sao?

Tôi tin rằng hầu hết mọi người đều hiểu rằng GC chính chỉ nhắm đến thế hệ cũ và GC đầy đủ sẽ kích hoạt GC nhỏ trước. Điều này có đúng không? Tôi đề cập đến phần phân tích và giới thiệu về R và tóm tắt những giải thích cũng như kết luận phân tích có liên quan.

Từ góc độ cơ bản dựa trên HotSpotVM

Về việc triển khai HotSpot VM, thực tế chỉ có hai loại GC trong đó:

GC một phần (chế độ thu thập một phần)

GC một phần đại diện cho một chế độ không thu thập toàn bộ vùng GC.

  • GC thế hệ trẻ (Chế độ thu thập thế hệ mới): Nó chủ yếu là trình thu thập GC tái chế các đối tượng bộ nhớ trong phạm vi Thế hệ trẻ.
  • GC thế hệ cũ/có thuê (chế độ thu thập thế hệ cũ): Nó chủ yếu nhằm mục đích tái chế trình thu gom rác GC trong phạm vi Thế hệ cũ/có thuê (Bộ sưu tập đồng thời của CMS là chế độ này).
  • GC thế hệ hỗn hợp (chế độ thu thập thế hệ hỗn hợp): Thu thập GC của toàn bộ thế hệ trẻ và một phần thế hệ cũ. Chỉ G1 mới có chế độ này

GC đầy đủ (chế độ thu thập đầy đủ)

GC đầy đủ thể hiện tập hợp toàn bộ vùng heap thời gian chạy + vùng phương thức + bộ nhớ ngoài vùng nhớ heap trực tiếp của JVM trong phạm vi tổng thể. (Thậm chí có thể hiểu là hầu hết vùng dữ liệu nằm trong phạm vi của quy trình JVM).

Nó sẽ bao gồm tất cả các chế độ và lĩnh vực bao gồm: Young Gen (thế hệ mới), Tenured Gen (thế hệ cũ), Perm/Meta Gen (meta space) (phiên bản trước và sau JDK8) và các chế độ thu gom rác GC toàn cầu khác.

Trong trường hợp bình thường, Major GC thường tương đương với Full GC, thu thập toàn bộ vùng GC. Nhưng nếu chúng ta bắt đầu từ các chi tiết cơ bản của HotSpot VM, nếu ai đó nói lại "GC chính", hãy hỏi xem họ muốn tham khảo GC đầy đủ hay GC cũ/Tenured ở trên.

Dựa trên chiến lược GC thế hệ đơn giản nhất

Điều kiện kích hoạt là: GC trẻ

Theo cách triển khai Serial GC của HotSpot VM, nó được kích hoạt khi sự khác biệt Eden ở thế hệ Young đạt đến ngưỡng (được kiểm soát bởi một tỷ lệ phần trăm nhất định).

Lưu ý: Một số đối tượng còn sót lại trong Young GC sẽ được thăng cấp lên Old Gen/Tenured Gen, do đó việc sử dụng Old Gen thường sẽ tăng lên sau Young GC.

Điều kiện kích hoạt là: GC đầy đủ
  1. Khi chuẩn bị kích hoạt GC Trẻ, nếu bạn thấy số liệu thống kê cho thấy dung lượng còn lại của Young Old/Tenured Gen trước đó lớn thì GC Trẻ sẽ không được kích hoạt mà sẽ kích hoạt Full GC (vì trong GC của HotSpot VM, ngoại trừ CMS Ngoại trừ bộ sưu tập đồng thời, các GC khác có thể thu thập Thế hệ Cũ/Tenured sẽ thu thập toàn bộ vùng heap GC cùng lúc, bao gồm cả thế hệ Young, do đó không cần phải kích hoạt trước một GC Trẻ riêng biệt );

  2. Nếu có gen Perm/Meta, GC đầy đủ phải được kích hoạt khi gen Perm/Meta phân bổ không gian nhưng không có đủ không gian.

  3. Phương thức System.gc() hoặc GC đi kèm với Heap Dump cũng kích hoạt Full GC theo mặc định. Các điều kiện kích hoạt cho các GC không đồng thời khác trong HotSpot VM phức tạp hơn nhưng nguyên tắc chung vẫn giống như đã đề cập ở trên.

Lưu ý: Trong khung Parallel Scavenge (-XX:+UseParallelGC), mặc định là thực thi GC trẻ một lần trước khi kích hoạt GC đầy đủ và cho phép ứng dụng chạy trong một thời gian ngắn giữa hai GC, để giảm thời gian tạm dừng của Full GC. GC. thời gian (vì GC non sẽ cố gắng dọn dẹp các đối tượng rác của Young Gen, giảm khối lượng công việc quét của Full GC). Tham số VM kiểm soát hành vi này là -XX:+ScavengeBeforeFullGC.

Điều kiện kích hoạt là: GC đồng thời

Các điều kiện kích hoạt của GC đồng thời là khác nhau. Lấy CMS GC làm ví dụ, nó chủ yếu kiểm tra việc sử dụng Old Gen một cách thường xuyên. Khi mức sử dụng vượt quá tỷ lệ kích hoạt, CMS GC sẽ được bắt đầu thu thập Old Gen đồng thời.

Danh sách các chế độ GC tương ứng với bộ thu GC

GC nối tiếp, GC song song, CMS và G1 do Hotspot JVM triển khai gần như có thể tương ứng với sự kết hợp nhất định giữa các thuật toán GC trẻ và GC cũ;

  • Thuật toán Serial GC: Serial Young GC + Serial Old GC (thực chất là GC đầy đủ toàn cầu);
  • Thuật toán GC song song: Parallel Young GC + non-parallel PS MarkSweep GC / Parallel Old GC (hai cái này thực chất là Global Full GC). Việc lựa chọn PS MarkSweep GC hoặc Parallel Old GC được kiểm soát bởi tham số UseParallelOldGC;
  • Thuật toán CMS: ParNew (Young) GC + CMS (Old) GC (kết quả cõng trên ParNew/các đối tượng còn sót lại của thế hệ cũ chỉ được ghi lại, không được nén) + GC đầy đủ cho thuật toán CMS (để đối phó với CMS GC lõi) vào những thời điểm nhất định) Nếu không vội vàng sẽ tốn rất nhiều chi phí);
  • G1 GC: GC trẻ + GC hỗn hợp (thế hệ mới, cộng với một phần của thế hệ cũ) + GC đầy đủ cho thuật toán G1 GC (để xử lý thuật toán G1 GC không vội vàng vào một số thời điểm nhất định và rất tốn kém);

Tóm tắt kích hoạt chế độ tái chế GC

  • Sau khi tìm ra các kết hợp trên, chúng ta hãy xem xét các điều kiện kích hoạt của các thuật toán GC khác nhau. Nói một cách đơn giản, điều kiện kích hoạt là vùng tương ứng của một thuật toán GC nhất định đã đầy hoặc được dự đoán là gần đầy. Ví dụ,
    • Nguyên nhân gây ra nhiều GC trẻ là khu vực vườn địa đàng đã đầy;
    • Trình kích hoạt GC/PS MarkSweep GC/PS Cũ nối tiếp là để dự đoán rằng tổng kích thước của các đối tượng được thăng cấp vượt quá kích thước còn lại của thế hệ cũ khi GC trẻ được thực thi;
    • Điều kiện kích hoạt để đánh dấu CMS GC ban đầu là tỷ lệ sử dụng thế hệ cũ vượt quá một giá trị nhất định;
    • Điều kiện kích hoạt để đánh dấu ban đầu của G1 GC là tỷ lệ sử dụng Heap vượt quá một giá trị nhất định;
    • Lý do kích hoạt thuật toán Full GC cho CMS và thuật toán GC đầy đủ cho G1 GC là hiển nhiên, đó là các thuật toán ưa thích của 4.3 và 4.4 không còn khả dụng nữa, vì vậy chúng tôi chỉ có thể thực hiện GC toàn cầu (tin tôi đi, đây là rất chậm! Cái này rất chậm) ! Cái này chậm!

[Điểm yếu và cạm bẫy] Mối quan hệ giữa -XX:+DisableExplicitGC và bộ nhớ trực tiếp của NIO

Nhiều người đã thấy tham số này được sử dụng trong các gợi ý điều chỉnh JVM phải không? Nhưng tại sao bạn nên sử dụng nó, khi nào bạn nên sử dụng nó và khi nào bạn sẽ rơi vào bẫy nếu sử dụng nó?

  1. Đầu tiên bạn cần hiểu rõ vai trò của thông số này. Trong quá trình triển khai cụ thể của Oracle/Sun JDK, tác dụng mặc định của System.gc() là kích hoạt một GC đầy đủ dừng lại như chúng ta đã biết ở trên, đó là thực hiện thu thập rác bộ nhớ trên toàn bộ vùng GC. .

  2. Thứ ba, nếu tham số -XX:+DisableExplicitGC được sử dụng, lệnh gọi tới System.gc() sẽ trở thành một lệnh gọi trống và hoàn toàn không kích hoạt bất kỳ GC nào (nhưng bản thân chi phí hoạt động của "cuộc gọi hàm" vẫn tồn tại ~ ).

    • Tại sao sử dụng tham số này? Lý do chính là để ngăn chặn một số sinh viên mới viết lệnh gọi System.gc() ở mọi nơi trong mã và can thiệp vào hoạt động bình thường của chương trình.
      1. Một số ứng dụng có thể chạy bình thường trong một ngày mà không có GC đầy đủ, nhưng chúng phải tạm dừng liên tục vì ai đó gọi System.gc() trong mã.
      2. Đôi khi các lệnh gọi này được viết trong các thư viện hoặc framework nhất định và nếu bạn không thể thay đổi mã của chúng nhưng không muốn bị làm phiền bởi các lệnh gọi này thì bạn cũng sẽ sử dụng tham số này.

-XX:+DisableExplicitGC Có vẻ như tham số này phải luôn được bật. Cạm bẫy là gì?

Nó sẽ xảy ra khi đồng thời thỏa mãn ba điều kiện sau

  1. Các đối tượng riêng của ứng dụng trong vùng GC hoạt động tốt và trong các trường hợp bình thường, GC đầy đủ sẽ không xuất hiện trong một thời gian dài.
  2. Ứng dụng sử dụng bộ nhớ trực tiếp của NIO một cách rộng rãi và thường xuyên và áp dụng nhiều lần cho DirectByteBuffer.
  3. -XX:+DisableExplicitGC được sử dụng.

Những hiện tượng có thể quan sát được là:

                        
                          java.lang.OutOfMemoryError: Bộ nhớ đệm trực tiếp tại java.nio.Bits.reserveMemory(Bits.java:633) tại java.nio.DirectByteBuffer.(DirectByteBuffer.java:98) tại java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:288)  

                        
                      
Hãy dùng một trường hợp để phân tích hiện tượng này:
                        
                          nhập java.nio.*; lớp công khai DisableExplicitGCDemo { công khai tĩnh void main(String[] args) { cho (int i = 0; i < 100000; i++) { ByteBuffer.allocateDirect(128); } System.out.println("Hoàn thành"); } }  

                        
                      

Sau đó biên dịch và chạy.

                        
                          $ java -version java version "1.6.0_25" Java(TM) SE Runtime Environment (bản dựng 1.6.0_25-b06) Java HotSpot(TM) 64-Bit Server VM (bản dựng 20.0-b11, chế độ hỗn hợp) $ javac DisableExplicitGCDemo.java $ java -XX:MaxDirectMemorySize=10m -XX:+PrintGC -XX:+DisableExplicitGC DisableExplicitGCDemo Ngoại lệ trong luồng "main" java.lang.OutOfMemoryError: Bộ nhớ đệm trực tiếp tại java.nio.Bits.reserveMemory(Bits.java:633) tại java.nio.DirectByteBuffer.(DirectByteBuffer.java:98) tại java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:288) tại DisableExplicitGCDemo.main(DisableExplicitGCDemo.java:6) $ java -XX:MaxDirectMemorySize=10m -XX:+PrintGC DisableExplicitGCDemo [GC 10996K->10480K(120704K), 0,0433980 giây] [GC đầy đủ 10480K->10415K(120704K), 0,0359420 giây] Xong  

                        
                      
  • Có thể thấy, cùng một chương trình có thể chạy bình thường mà không cần -XX:+DisableExplicitGC, nhưng với tham số này, OOM xảy ra. -XX:MaxDirectMemorySize=10m giới hạn không gian mà DirectByteBuffer có thể phân bổ để các vấn đề có thể biểu hiện dễ dàng hơn. Nếu không sử dụng tham số này thì sẽ mất một khoảng thời gian để chạy.

  • Vòng lặp liên tục áp dụng cho DirectByteBuffer nhưng không có tham chiếu, vì vậy các DirectByteBuffer này phải đáp ứng các điều kiện cho GC ngay khi chúng được tạo và chúng phải được tái chế vào lần chạy GC tiếp theo.

  • Trên thực tế, nó không đơn giản như vậy. DirectByteBuffer là một đối tượng "tảng băng trôi" điển hình, có nghĩa là mặc dù đối tượng Java của nó rất nhỏ và vô tội, nhưng nó được liên kết với một lượng tài nguyên bộ nhớ riêng nhất định và các tài nguyên này không nằm dưới sự kiểm soát của GC và yêu cầu phải trả tiền riêng. chú ý kiểm soát.

Những sinh viên chưa quen với cách JVM sử dụng bộ nhớ riêng có thể nghiên cứu bài phát biểu này, "Tất cả bộ nhớ riêng sẽ đi về đâu".

[Vấn đề điểm mù] Vấn đề tái chế DirectByteBuffer

Trong quá trình triển khai Oracle/Sun JDK, có một số điều đáng chú ý về DirectByteBuffer.

  1. DirectByteBuffer không có bộ hoàn thiện và công việc dọn dẹp bộ nhớ riêng của nó được tự động hoàn thành thông qua sun.misc.Cleaner.
  2. sun.misc.Cleaner là một công cụ dọn dẹp dựa trên PhantomReference, nhẹ hơn công cụ hoàn thiện thông thường.

"Một trình dọn dẹp theo dõi một đối tượng tham chiếu và đóng gói một đoạn mã dọn dẹp tùy ý. Một thời gian sau khi GC phát hiện ra rằng đối tượng tham chiếu của trình dọn dẹp đã trở thành đối tượng có thể truy cập ảo, luồng xử lý tham chiếu sẽ chạy trình dọn dẹp."

Nhận xét mã nguồn
                        
                          /** * Trình dọn dẹp dựa trên tham chiếu ảo mục đích chung. * * 

Trình dọn dẹp là giải pháp thay thế nhẹ và mạnh mẽ hơn cho quá trình hoàn thiện. * Chúng nhẹ vì chúng không được VM tạo ra và do đó không * yêu cầu tạo lệnh gọi JNI và vì mã dọn dẹp của chúng được luồng xử lý tham chiếu gọi trực tiếp chứ không phải luồng hoàn thiện. Chúng mạnh mẽ hơn vì chúng sử dụng tham chiếu ảo, * loại đối tượng tham chiếu yếu nhất, do đó tránh được các vấn đề về thứ tự khó chịu vốn có trong quá trình hoàn thiện. * *

Trình dọn dẹp theo dõi đối tượng tham chiếu và đóng gói một thunk mã dọn dẹp tùy ý. Sau một thời gian, GC phát hiện ra rằng tham chiếu của trình dọn dẹp đã * trở thành có thể tiếp cận được bằng phantom, luồng xử lý tham chiếu sẽ chạy trình dọn dẹp. * Trình dọn dẹp cũng có thể được gọi trực tiếp; chúng an toàn cho luồng và đảm bảo rằng * chúng chạy thunk của chúng nhiều nhất một lần. * *

Trình dọn dẹp không phải là giải pháp thay thế cho quá trình hoàn thiện. Chúng chỉ nên được sử dụng * khi mã dọn dẹp cực kỳ đơn giản và dễ hiểu. * Các trình dọn dẹp không tầm thường không được khuyến khích vì chúng có nguy cơ chặn * luồng xử lý tham chiếu và làm chậm quá trình dọn dẹp và hoàn thiện thêm. * * * @author Mark Reinhold * @version %I%, %E% */

HotSpot VM trong Oracle/Sun JDK sẽ chỉ thực hiện Xử lý tham chiếu trên các đối tượng trong Thế hệ cũ trong GC thế hệ cũ (GC đầy đủ/GC chính hoặc GC đồng thời) và sẽ chỉ thực hiện Xử lý tham chiếu trong GC trẻ/Thực hiện tham chiếu. xử lý các đối tượng trong Young Gen. GC đầy đủ sẽ thực hiện xử lý Tham chiếu trên Old Gen, có thể kích hoạt Cleaner để dọn sạch các đối tượng DirectByteBuffer đã chết.

  • Nếu GC đã lâu không được thực hiện hoặc chỉ GC trẻ được thực hiện thì công việc Cleaner sẽ không được kích hoạt trong Old Gen. Khi đó có thể Bộ nhớ gốc liên kết với DirectByteBuffer đã chết nhưng đã được thăng cấp lên Thế hệ cũ sẽ không được kích hoạt. Không thể phát hành kịp thời.

  • System.gc() được gọi rõ ràng trong quá trình phân bổ không gian cho DirectByteBuffer để buộc các đối tượng DirectByteBuffer vô dụng giải phóng bộ nhớ riêng liên quan của chúng thông qua Full GC.

                        
                          // Các phương thức này nên được gọi bất cứ khi nào bộ nhớ trực tiếp được phân bổ hoặc // giải phóng. Chúng cho phép người dùng kiểm soát lượng bộ nhớ trực tiếp // mà một tiến trình có thể truy cập. Tất cả các kích thước được chỉ định bằng byte. static void reserveMemory(long size) { synchronized (Bits.class) { if (!memoryLimitSet && VM.isBooted()) { maxMemory = VM.maxDirectMemory(); memoryLimitSet = true; } if (size <= maxMemory - reservedMemory) { reservedMemory += size; return; } } System.gc(); try { Thread.sleep(100); } catch (InterruptedException x) { // Khôi phục trạng thái ngắt Thread.currentThread().interrupt(); } synchronized (Bits.class) { if (reservedMemory + size > maxMemory) throw new OutOfMemoryError("Direct buffer memory"); reservedMemory += size; } }  

                        
                      

Phân tích tóm tắt

Các tính năng triển khai này làm cho Oracle/Sun JDK dựa vào System.gc() để kích hoạt GC nhằm đảm bảo rằng việc dọn dẹp DirectByteMemory có thể được hoàn thành kịp thời.

Nếu -XX:+DisableExplicitGC được bật, công việc dọn dẹp có thể không được hoàn thành kịp thời, do đó có cơ hội nhìn thấy OOM bộ nhớ trực tiếp, đó là tình huống được minh họa trong ví dụ trên. Chúng tôi thực sự đã gặp phải vấn đề như vậy trong môi trường sản xuất thực tế.

Nếu bạn đang sử dụng Oracle/Sun JDK và bộ nhớ trực tiếp được sử dụng ở bất kỳ đâu trong ứng dụng, hãy cẩn thận khi sử dụng -XX:+DisableExplicitGC. Nếu bạn sử dụng tham số này và gặp phải OOM bộ nhớ trực tiếp, bạn có thể thử loại bỏ tham số này để xem liệu bạn có thể tránh được OOM này hay không. Nếu lo lắng về việc Full GC thường xuyên do lệnh gọi System.gc() gây ra, bạn có thể thử tham số -XX:+ExplicitGCInvokesConcurrent được đề cập bên dưới.

Cuối cùng, bài viết này về phân tích chuyên sâu | [JVM Deep Series] [HotSpotVM Research Series] Phân tích các cạm bẫy khác nhau của "tham số tiêu chuẩn" của việc điều chỉnh JVM (khắc phục các điểm mù và điểm nhầm lẫn) "1" kết thúc tại đây. bạn muốn biết thêm về hồ sơ chuyên sâu | [Chuỗi sâu JVM] [Chuỗi nghiên cứu HotSpotVM] Phân tích các cạm bẫy khác nhau của "tham số tiêu chuẩn" để điều chỉnh JVM (khắc phục điểm mù và điểm nhầm lẫn) "1" Vui lòng tìm kiếm các bài viết CFSDN hoặc tiếp tục duyệt các bài viết liên quan. Hy vọng Hãy ủng hộ tôi. blog trong tương lai! .

34 4 0
tôi là một con chim nhỏ
Hồ sơ

Tôi là một lập trình viên xuất sắc, rất giỏi!

Nhận phiếu giảm giá taxi Didi miễn phí
Phiếu giảm giá taxi Didi
Chứng chỉ ICP Bắc Kinh số 000000
Hợp tác quảng cáo: 1813099741@qq.com 6ren.com
Xem sitemap của VNExpress