paint-brush
Phần mềm phát triển mạnh trừ khi bạn tiêu diệt nó trước: Tối ưu hóa sớm và câu chuyện về Java GCtừ tác giả@wasteofserver
520 lượt đọc
520 lượt đọc

Phần mềm phát triển mạnh trừ khi bạn tiêu diệt nó trước: Tối ưu hóa sớm và câu chuyện về Java GC

từ tác giả Frankie6m2024/04/06
Read on Terminal Reader

dài quá đọc không nổi

Đừng quá lạm dụng việc tối ưu hóa, hãy để ngôn ngữ giúp ích cho bạn. Kể chuyện. Nâng cấp Java giúp tăng hiệu suất miễn phí. Điểm chuẩn, luôn luôn.
featured image - Phần mềm phát triển mạnh trừ khi bạn tiêu diệt nó trước: Tối ưu hóa sớm và câu chuyện về Java GC
Frankie HackerNoon profile picture
0-item

LinkedList có nhanh hơn không? Tôi có nên hoán đổi `cho mỗi` bằng` iterator` không? `ArrayList` này có nên là `Array` không? Bài viết này ra đời nhằm đáp lại một sự tối ưu hóa ác độc đến mức nó đã vĩnh viễn khắc sâu vào trí nhớ của tôi.


Trước khi bắt đầu tìm hiểu Java và các cách giải quyết sự can thiệp, từ trình thu thập rác hoặc từ chuyển đổi ngữ cảnh, trước tiên chúng ta hãy xem qua các nguyên tắc cơ bản về viết mã cho tương lai của bạn.


Tối ưu hóa sớm là gốc rễ của mọi tội lỗi.


Bạn đã từng nghe nó trước đây; tối ưu hóa sớm là gốc rễ của mọi tội lỗi. Cũng đôi khi. Khi viết phần mềm, tôi có niềm tin vững chắc vào việc:


  1. mang tính mô tả nhất có thể ; bạn nên cố gắng thuật lại ý định như thể bạn đang viết một câu chuyện.


  2. tối ưu nhất có thể ; điều đó có nghĩa là bạn nên biết các nguyên tắc cơ bản của ngôn ngữ và áp dụng chúng cho phù hợp.

Càng mô tả càng tốt

Mã của bạn phải nói lên ý định và phần lớn liên quan đến cách bạn đặt tên cho các phương thức và biến.


 int[10] array1; // bad int[10] numItems; // better int[10] backPackItems; // great

Chỉ cần nhìn vào tên biến là bạn đã có thể suy ra chức năng.


Trong khi numItems là trừu tượng, backPackItems cho bạn biết rất nhiều về hành vi dự kiến.


Hoặc nói rằng bạn có phương pháp này:


 List<Countries> visitedCountries() { if(noCountryVisitedYet) return new ArrayList<>(0); } // (...) return listOfVisitedCountries; }

Theo như mã, điều này có vẻ ít nhiều ổn.


Chúng ta có thể làm tốt hơn không? Chúng tôi chắc chắn có thể!


 List<Countries> visitedCountries() { if(noCountryVisitedYet) return Collections.emptyList(); } // (...) return listOfVisitedCountries; }

Đọc Collections.emptyList() mang tính mô tả nhiều hơn new ArrayList<>(0);


Hãy tưởng tượng bạn đang đọc đoạn mã trên lần đầu tiên và tình cờ gặp mệnh đề bảo vệ để kiểm tra xem người dùng đã thực sự đến thăm các quốc gia hay chưa. Ngoài ra, hãy tưởng tượng điều này được chôn trong một lớp học dài, đọc Collections.emptyList() chắc chắn mang tính mô tả nhiều hơn new ArrayList<>(0) , bạn cũng đảm bảo rằng nó không thể thay đổi, đảm bảo mã máy khách không thể sửa đổi nó.

Tối ưu nhất có thể

Biết ngôn ngữ của bạn và sử dụng nó cho phù hợp. Nếu bạn cần double , không cần phải bọc nó trong đối tượng Double . Điều tương tự cũng xảy ra với việc sử dụng List nếu tất cả những gì bạn thực sự cần là Array .


Biết rằng bạn nên nối các Chuỗi bằng StringBuilder hoặc StringBuffer nếu bạn đang chia sẻ trạng thái giữa các luồng:


 // don't do this String votesByCounty = ""; for (County county : counties) { votesByCounty += county.toString(); } // do this instead StringBuilder votesByCounty = new StringBuilder(); for (County county : counties) { votesByCounty.append(county.toString()); }


Biết cách lập chỉ mục cơ sở dữ liệu của bạn. Dự đoán tắc nghẽn và bộ nhớ đệm phù hợp. Tất cả những điều trên là tối ưu hóa. Chúng là loại tối ưu hóa mà bạn nên biết và thực hiện với tư cách là những công dân đầu tiên.

Làm thế nào để bạn giết nó đầu tiên?

Tôi sẽ không bao giờ quên một bản hack mà tôi đã đọc cách đây vài năm. Sự thật mà nói, tác giả đã nhanh chóng quay lại, nhưng nó cho thấy rất nhiều điều xấu xa có thể xuất phát từ những ý định tốt.


 // do not do this, ever! int i = 0; while (i<10000000) { // business logic if (i % 3000 == 0) { //prevent long gc try { Thread.sleep(0); } catch (Ignored e) { } } }

Một công cụ thu gom rác từ địa ngục!


Bạn có thể đọc thêm về lý do và cách thức hoạt động của đoạn mã trên trong bài viết gốc . Mặc dù cách khai thác chắc chắn rất thú vị nhưng đây là một trong những điều bạn không bao giờ nên làm.


  • Hoạt động theo tác dụng phụ, Thread.sleep(0) không có mục đích gì trong khối này
  • Hoạt động bằng cách khai thác sự thiếu hụt mã ở hạ lưu
  • Đối với bất kỳ ai kế thừa mã này, nó thật tối nghĩa và kỳ diệu


Chỉ bắt đầu rèn thứ gì đó phức tạp hơn một chút nếu sau khi viết với tất cả các tối ưu hóa mặc định mà ngôn ngữ cung cấp , bạn gặp phải nút thắt cổ chai. Nhưng hãy tránh xa những cách pha chế như trên.


Giải thích về Garbage Collector trong tương lai của Java do Microsoft Copilot "tưởng tượng"


Làm thế nào để giải quyết vấn đề thu gom rác đó ?

Nếu sau khi mọi việc đã xong, Bộ thu gom rác vẫn là bộ phận gây phản kháng, thì đây là một số cách bạn có thể thử:


  • Nếu dịch vụ của bạn nhạy cảm với độ trễ đến mức bạn không thể cho phép GC, hãy chạy với "Epsilon GC" và tránh hoàn toàn GC .
    -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC


    Điều này rõ ràng sẽ tăng bộ nhớ của bạn cho đến khi bạn gặp ngoại lệ OOM, do đó, đó là một kịch bản ngắn hạn hoặc chương trình của bạn được tối ưu hóa để không tạo đối tượng


  • Nếu dịch vụ của bạn hơi nhạy cảm với độ trễ, nhưng dung sai cho phép cho phép mất một chút thời gian , hãy chạy GC1 và cung cấp cho nó nội dung như -XX:MaxGCPauseTimeMillis=100 (mặc định là 250 mili giây)

  • Nếu sự cố xuất phát từ các thư viện bên ngoài , chẳng hạn như một trong số chúng gọi System.gc() hoặc Runtime.getRuntime().gc() là các trình thu gom rác toàn cầu, bạn có thể ghi đè hành vi vi phạm bằng cách chạy với -XX:+DisableExplicitGC


  • Nếu bạn đang chạy trên JVM trên 11, hãy thử Z Garbage Collector (ZGC) , những cải tiến về hiệu suất là rất lớn! -XX:+UnlockExperimentalVMOptions -XX:+UseZGC . Bạn cũng có thể muốn kiểm tra điểm chuẩn JDK 21 GC này.


PHIÊN BẢN BẮT ĐẦU

PHIÊN BẢN KẾT THÚC

GC MẶC ĐỊNH

Java 1

Java 4

Máy thu gom rác nối tiếp

Java 5

Java 8

Máy thu gom rác song song

Java 9

đang diễn ra

Máy thu gom rác G1


Lưu ý 1: kể từ Java 15, ZGC đã sẵn sàng sản xuất , nhưng bạn vẫn phải kích hoạt nó một cách rõ ràng bằng -XX:+UseZGC .


Lưu ý 2: VM coi máy là lớp máy chủ nếu VM phát hiện nhiều hơn hai bộ xử lý và kích thước heap lớn hơn hoặc bằng 1792 MB. Nếu không phải lớp máy chủ, nó sẽ mặc định là Serial GC .


Về bản chất, hãy chọn điều chỉnh GC khi rõ ràng rằng các hạn chế về hiệu suất của ứng dụng có liên quan trực tiếp đến hành vi thu gom rác và bạn có kiến thức chuyên môn cần thiết để thực hiện các điều chỉnh sáng suốt. Mặt khác, hãy tin tưởng vào cài đặt mặc định của JVM và tập trung vào việc tối ưu hóa mã cấp ứng dụng.

u/shiphe - bạn sẽ muốn đọc bình luận đầy đủ


Các thư viện liên quan khác mà bạn có thể muốn khám phá:

Khai thác điểm chuẩn vi mô Java (JMH)

Nếu bạn tối ưu hóa cảm giác mà không có bất kỳ điểm chuẩn thực sự nào thì bạn đang tự làm hại chính mình. JMH là thư viện Java trên thực tế để kiểm tra hiệu suất thuật toán của bạn. Sử dụng nó.

Java-Thread-Affinity

Ghim một tiến trình vào một lõi cụ thể có thể cải thiện số lần truy cập bộ đệm. Nó sẽ phụ thuộc vào phần cứng cơ bản và cách xử lý dữ liệu thường ngày của bạn. Tuy nhiên, thư viện này giúp việc triển khai trở nên dễ dàng đến mức nếu một phương pháp sử dụng nhiều CPU đang cản trở bạn, bạn sẽ muốn thử nghiệm nó.

Bộ gây rối LMAX

Đây là một trong những thư viện mà ngay cả khi bạn không cần nó, bạn vẫn muốn nghiên cứu. Ý tưởng là cho phép đồng thời có độ trễ cực thấp. Nhưng cách nó được triển khai, từ sự đồng cảm cơ học đến bộ đệm vòng, mang lại rất nhiều khái niệm mới. Tôi vẫn nhớ lần đầu tiên tôi phát hiện ra nó, bảy năm trước, tôi đã thức trắng đêm để tiêu hóa nó.

Netflix jvmquake

Tiền đề của jvmquake là khi mọi thứ không ổn với JVM, bạn muốn nó chết và không bị treo. Một vài năm trước, tôi đã chạy mô phỏng trên cụm HTCondor có hạn chế về bộ nhớ và đôi khi, công việc sẽ bị kẹt do lỗi "hết bộ nhớ".


Thư viện này buộc JVM phải chết, cho phép bạn xử lý lỗi thực tế. Trong trường hợp cụ thể này, HTCondor sẽ tự động lên lịch lại công việc.

suy nghĩ cuối cùng

Mã khiến tôi viết bài này? Tôi đã viết tệ hơn nhiều. Tôi vẫn làm. Điều tốt nhất chúng ta có thể hy vọng là liên tục gây ra ít rắc rối hơn.


Tôi hy vọng sẽ bất mãn khi nhìn vào mã của chính mình sau vài năm nữa.


Và đó là một dấu hiệu tốt.



Chỉnh sửa & Cảm ơn bạn:


Cũng được xuất bản trên Wasteofserver.com