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:
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.
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.
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ó.
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.
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.
Thread.sleep(0)
không có mục đích gì trong khối này
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.
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
-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 đủ
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ó.
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ó.
Đâ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ó.
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.
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.
visitedCountries()
và phần giải thích chi tiết.Cũng được xuất bản trên Wasteofserver.com