2015-05-25 11 views
7

Trong khi di chuyển từ CMS sang G1 cho một số ứng dụng của chúng tôi, tôi nhận thấy rằng một trong số chúng bị thời gian khởi động kéo dài bởi yếu tố 4. Thời gian dừng ứng dụng do chu trình GC không phải là nguyên nhân. Khi so sánh hành vi của ứng dụng, tôi đã giải tán rằng cái này mang một con số khổng lồ 250 triệu đối tượng trực tiếp sau khi khởi động (trong một đống 12G). Điều tra thêm cho thấy rằng ứng dụng có tốc độ bình thường trong 5 triệu lần phân bổ đầu tiên, nhưng hiệu suất giảm xuống nhiều hơn và nhiều hơn khi các nhóm vật thể sống lớn hơn.Hiệu suất phân bổ có làm giảm đi một số lượng lớn các cá thể trực tiếp khi sử dụng G1 không?

Các thử nghiệm khác cho thấy rằng khi đạt đến một ngưỡng nhất định của các đối tượng trực tiếp, việc phân bổ các đối tượng mới thực sự sẽ chậm lại khi sử dụng G1. Tôi thấy rằng tăng gấp đôi số lượng các đối tượng sống dường như đặt một yếu tố khoảng 2,5 vào thời gian cần thiết cho phân bổ đó. Với các động cơ GC khác, yếu tố chỉ là 2. Điều này thực sự sẽ giải thích sự suy giảm.

Có hai vấn đề mà làm cho tôi nghi ngờ kết luận rằng, mặc dù:

  • Ngưỡng khoảng khoảng 5 triệu trường hợp sống dường như có liên quan đến các đống như một toàn thể. Với G1, tôi có thể mong đợi bất kỳ ngưỡng xuống cấp nào như vậy có liên quan đến một khu vực, không phải cho toàn bộ đống.
  • Tôi đã xem xét các tài liệu trong web giải thích (hoặc ít nhất là nói rõ) hành vi này, nhưng tôi không tìm thấy. Tôi thậm chí không tìm thấy các khuyến nghị của loại 'Có hơn xxx đối tượng sống là điều ác'.

Vì vậy: sẽ rất tuyệt nếu ai đó có thể cho tôi biết rằng các quan sát của tôi là chính xác và có thể chỉ cho tôi một số tài liệu giải thích hoặc một số đề xuất liên quan đến lĩnh vực này. Hoặc, cách khác, ai đó nói với tôi điều tôi đang làm sai. :)

Đây là một trường hợp thử nghiệm ngắn (chạy nhiều lần, có giá trị trung bình, trừ lần thu gom rác thải hiển thị):

import java.util.HashMap; 

/** 
    * Allocator demonstrates the dependency between number of live objects 
    * and allocation speed, using various GC algorithms. 
    * Call it using, e.g.: 
    * java Allocator -Xmx12g -Xms12g -XX:+PrintGCApplicationStoppedTime -XX:+UseG1GC 
    * java Allocator -Xmx12g -Xms12g -XX:+PrintGCApplicationStoppedTime 
    * Deduct stopped times from execution time. 
    */ 
public class Allocator { 

public static void main(String[] args) { 
    timer(2000000, true); 
    for (int i = 1000000; i <= 32000000; i*=2) { 
     timer(i, false); 
    } 
    for (int i = 32000000; i >= 1000000; i/=2) { 
     timer(i, false); 
    } 
} 

private static void timer(int num, boolean warmup) { 
    long before = System.currentTimeMillis(); 
    Allocator a = new Allocator(); 
    int size = a.allocate(num); 
    long after = System.currentTimeMillis(); 
    if (!warmup) { 
     System.out.println("Time needed for " + num + " allocations: " 
      + (after - before) + " millis. Map size = " + size); 
    } 
} 

private int allocate(int numElements) { 
    HashMap<Integer, String> map = new HashMap<>(2*numElements); 
    for (int i = 0; i < numElements; i++) { 
     map.put(i, Integer.toString(i)); 
    } 
    return map.size(); 
} 

} 
+0

Nhật ký GC (qua 'PrintGCDetails') và đề cập đến phiên bản java bạn đang sử dụng sẽ hữu ích – the8472

+0

Bản ghi đầy đủ GC của ứng dụng thực tế quá lớn.:) Nhưng dù sao, trường hợp thử nghiệm ở trên thể hiện hành vi. Xin lưu ý rằng vấn đề ở đây là * không * GC tạm dừng. - Tôi sử dụng Java 8 Update 45. Hành vi giống hệt với Windows và Linux. – malamut

+1

câu hỏi là liệu trường hợp kiểm tra của bạn có gây ra các vấn đề tương tự như khối lượng công việc thực tế của bạn hay không. có một hashmap duy nhất giữ cho hàng triệu đối tượng sẽ tạo ra một mảng tham chiếu rất lớn bên trong bản đồ băm, có thể sẽ yêu cầu một vùng cho chính nó, do đó làm cho hầu hết các tham chiếu từ bảng hashmap đến các nút của nó. giữ sách. – the8472

Trả lời

1

Như đã thảo luận trong các ý kiến ​​trên:

Test- của bạn trường hợp trước khi phân bổ các mảng tham chiếu rất lớn tồn tại lâu dài và về cơ bản chiếm một khu vực của riêng chúng (chúng có thể kết thúc ở vùng cũ hoặc các vùng humongous) và sau đó điền chúng với hàng triệu đối tượng bổ sung có khả năng sống một vùng khác.

Điều này tạo ra rất nhiều tài liệu tham khảo nhiều vùng, mà G1 có thể xử lý với số lượng vừa phải nhưng không phải trong hàng triệu mỗi vùng.

Các khu vực có kết nối cao cũng được coi là tốn kém để thu thập bởi các chẩn đoán của G1 và do đó ít có khả năng được thu thập ngay cả khi chúng hoàn toàn bao gồm rác.

Phân bổ các đối tượng lại với nhau để giảm tham chiếu vùng chéo.

Không kéo dài tuổi thọ của chúng quá nhiều (ví dụ bằng cách đặt chúng vào bộ nhớ đệm) cũng cho phép chúng chết trong GC thế hệ trẻ, dễ thu thập hơn các khu vực cũ do tự nhiên tích lũy các đối tượng được tham chiếu từ vùng.

Vì vậy, tất cả trong tất cả các trường hợp thử nghiệm của bạn khá thù địch với bản chất dựa trên khu vực của G1.

+0

Có, trường hợp thử nghiệm là thù địch, nhưng ứng dụng thực tế là thù địch theo cùng một cách chính xác, do đó, trường hợp thử nghiệm đã làm công việc của mình. :) Tôi đã học được rằng, với G1, nó phải được đảm bảo rằng số lượng các tài liệu tham khảo xuyên khu vực không phát triển vượt xa một triệu. Cách dễ nhất để đạt được điều này thường là đảm bảo rằng tổng số đối tượng trực tiếp không phát triển thành nhiều triệu. – malamut

Các vấn đề liên quan