2010-01-13 51 views
32

Tôi muốn đăng ký cuộc gọi lại với JVM để tôi biết khi nào việc thu gom rác đang diễn ra. Có cách nào để làm điều này không?Thông báo thu gom rác thải?

EDIT: Tôi muốn làm điều này để tôi có thể đăng xuất khi thu thập rác xảy ra trong nhật ký ứng dụng của mình, vì vậy tôi có thể xem liệu nó có tương quan với các vấn đề tôi thấy không. Bật -Xloggc là hữu ích, nhưng có một chút khó khăn khi tích hợp thời gian từ nhật ký GC (sử dụng giây kể từ khi bắt đầu ứng dụng) vào nhật ký ứng dụng chính của tôi.

EDIT tháng 4 năm 2012: Kể từ Java7u4, bạn có thể nhận thông báo từ GarbageCollectorMXBean (số đẹp example).

+0

Lý tưởng nhất, một nhà phát triển không cần phải quan tâm khi GC xảy ra. Bạn có thể thực hiện một số thiết lập trong các đối số JVM. Chỉ vì tò mò, bạn đang cố gắng làm gì? Có thể có một sự kiện tốt hơn để đăng ký. – Jay

+13

@Jay, chúng ta không sống trong một thế giới lý tưởng. Nếu bạn thực sự quan tâm đến dịch vụ của bạn làm việc, bạn có thể muốn báo động khi thu gom rác bắt đầu xảy ra quá thường xuyên vì nó có thể là một dấu hiệu của vấn đề. – tster

+1

Câu hỏi hay. Nó sẽ tốt đẹp nếu GC thông báo có thể được tích hợp với một cân bằng tải để tránh gửi lưu lượng truy cập đến thể hiện trong GC. –

Trả lời

17

Kể từ Java7u4, bạn có thể nhận thông báo từ GarbageCollectorMXBean. Xem http://docs.oracle.com/javase/7/docs/jre/api/management/extension/com/sun/management/GarbageCollectionNotificationInfo.html

+1

Điều đáng nói là nó thường nhanh hơn để xem MemoryPoolMXBean và xem khi sử dụng bộ nhớ giảm xuống. – Xorlev

+0

Trong khi MemoryPoolMXBean nhanh hơn nhiều, bạn chỉ đơn giản là không biết khi nào GC được chạy.Nếu điều quan trọng là bạn phải biết những thứ như mức sử dụng bộ nhớ tối đa của ứng dụng, thì bạn sẽ muốn kiểm tra việc sử dụng bộ nhớ ngay trước và sau khi thu gom rác. Nếu bạn bình chọn, bạn sẽ không biết chắc chắn. Chỉ cần $ 0,02 của tôi :) – CodeBlind

1

Có một số interesting article on Javalobby thảo luận về một phương pháp để thực hiện việc này.

+0

Cách tiếp cận thú vị, nhưng nó giới thiệu một số chi phí và chỉ thông báo cho bạn khi một đối tượng cụ thể được thu thập. Tôi muốn có thông báo khi GC xảy ra, ở cùng tần suất lệnh -Xloggc ghi vào một tệp. –

3

Có vẻ như bạn có thể sử dụng MemoryPoolMXBean và đặt ngưỡng sử dụng bộ sưu tập thành 1. Điều này sẽ cung cấp cho bạn thông báo bất cứ khi nào gc chạy và vẫn còn ít nhất một byte bộ nhớ đang sử dụng.

http://java.sun.com/j2se/1.5.0/docs/api/java/lang/management/MemoryPoolMXBean.html

Dường như điều này không làm việc với tất cả các nhà sưu tập rác, mặc dù.

1

Không có cách nào tiêu chuẩn để chương trình của riêng bạn nhận thông tin từ JVM về thu gom rác thải. Bất kỳ API nào như vậy đều là nhà cung cấp cụ thể.

Tại sao cơ sở bạn tìm thấy không đủ?

+0

JVM TI là tiêu chuẩn cho Java 5+ nếu tôi nhớ chính xác. Bạn có muốn một chương trình lắng nghe * chính nó * với điều đó không? –

+0

Tôi không thể tìm thấy liên kết cuối cùng nhưng JVM TI dường như được thực hiện bởi hầu hết các JVM: Sun, IBM (http://publib.boulder.ibm.com/infocenter/javasdk/v6r0/index.jsp?topic=/ com.ibm.java.doc.diagnostics.60/diag/tools/jvmti.html), Oracle/BEA, Harmony (và có thể nhiều hơn). –

+0

Ồ, tôi hiểu ý của bạn là gì. Nhưng một đại lý có thể là chương trình của riêng bạn :) –

0

Nếu bạn đang xem đây là công cụ chẩn đoán, tôi khuyên bạn nên chuyển hướng nhật ký ứng dụng của mình tới StdOut và sau đó chuyển hướng cả StdOut và StdErr vào tệp. Điều này sẽ cung cấp cho bạn chi tiết về nhật ký JVM, mà không buộc bạn phải thay đổi mã ứng dụng của mình.

+0

Khi tôi viết bài này, tôi bắt đầu tự hỏi liệu có thể nhật ký JVM có thể được chuyển hướng vào khung đăng nhập ứng dụng của bạn hay không. Bạn đang sử dụng gì cho trình đăng ký ứng dụng? – kdgregory

+0

Chúng tôi đang sử dụng log4j –

10

Tôi nghĩ rằng cách tiêu chuẩn là sử dụng JVM Tool Interface (JVM TI) để viết tác nhân có cuộc gọi lại GC bắt đầu và ghi lại thời gian từ nó (xem GetTime). Lưu ý rằng Garbage Collection Start event chỉ được gửi cho các GC đầy đủ.

Các đại lý JVM TI mẫu có sẵn trong thư mục demo của bản tải xuống JDK 5.0 hoặc JDK 6. Bài viết công nghệ The JVM Tool Interface (JVM TI): How VM Agents Work là một tài nguyên rất khác. Ngoài ra, hãy xem Creating a Debugging and Profiling Agent with JVMTI.

+0

Đề xuất tốt; Tôi chưa từng nghe đến JVM TI. Tuy nhiên, ứng dụng của chúng tôi hầu như không bao giờ làm đầy đủ, vì vậy tôi muốn biết về bộ sưu tập gen 1. Bạn nghĩ gì về ý tưởng MemoryPoolMXBean? –

2

Khi nhận sự kiện JVMTI cho Thu gom rác, JVM bị dừng kỹ thuật nên không thể gọi lại trình nghe Java qua JNI .... tác nhân này in ra thời gian tại GC bắt đầu và kết thúc với độ phân giải cao hơn tiết GC trên Sun JVM.

#include <stdlib.h> 
#include <time.h> 
#include <sys/time.h> 
#include "jvmti.h" 

void printGCTime(const char* type) { 

    struct timeval tv; 
    gettimeofday(&tv, NULL); 

    struct tm localTime; 
    localtime_r(&tv.tv_sec, &localTime); 

    char *startTime = calloc(1, 128); 

    strftime(startTime, (size_t) 128, "%a %b %d %Y %H:%M:%S", &localTime); 

    fprintf(stderr, "GC %s: %s.%06d\n", type, startTime, (int)tv.tv_usec); 
    fflush(stderr); 

    if(startTime) free(startTime); 

} 

void JNICALL 
garbageCollectionStart(jvmtiEnv *jvmti_env) { 

    printGCTime("Start "); 

} 

void JNICALL 
garbageCollectionFinish(jvmtiEnv *jvmti_env) { 

    printGCTime("Finish"); 

} 


JNIEXPORT jint JNICALL 
Agent_OnLoad(JavaVM * jvm, char *options, void *reserved) 
{ 
    jvmtiEnv *jvmti_env; 

    jint returnCode = (*jvm)->GetEnv(jvm, (void **) &jvmti_env, 
     JVMTI_VERSION_1_0); 



    if (returnCode != JNI_OK) 
    { 
     fprintf(stderr, 
      "The version of JVMTI requested (1.0) is not supported by this JVM.\n"); 
     return JVMTI_ERROR_UNSUPPORTED_VERSION; 
    } 


    jvmtiCapabilities *requiredCapabilities; 

    requiredCapabilities = (jvmtiCapabilities*) calloc(1, sizeof(jvmtiCapabilities)); 
    if (!requiredCapabilities) 
     { 
     fprintf(stderr, "Unable to allocate memory\n"); 
     return JVMTI_ERROR_OUT_OF_MEMORY; 
     } 

    requiredCapabilities->can_generate_garbage_collection_events = 1; 

    if (returnCode != JNI_OK) 
    { 
     fprintf(stderr, "C:\tJVM does not have the required capabilities (%d)\n", 
      returnCode); 
     exit(-1); 
    } 



    returnCode = (*jvmti_env)->AddCapabilities(jvmti_env, requiredCapabilities); 


    jvmtiEventCallbacks *eventCallbacks; 

    eventCallbacks = calloc(1, sizeof(jvmtiEventCallbacks)); 
    if (!eventCallbacks) 
    { 
     fprintf(stderr, "Unable to allocate memory\n"); 
     return JVMTI_ERROR_OUT_OF_MEMORY; 
    } 

    eventCallbacks->GarbageCollectionStart = &garbageCollectionStart; 
    eventCallbacks->GarbageCollectionFinish = &garbageCollectionFinish; 


    returnCode = (*jvmti_env)->SetEventCallbacks(jvmti_env, 
     eventCallbacks, (jint) sizeof(*eventCallbacks)); 


    if (returnCode != JNI_OK) 
    { 
     fprintf(stderr, "C:\tError setting event callbacks (%d)\n", 
      returnCode); 
     exit(-1); 
    } 

    returnCode = (*jvmti_env)->SetEventNotificationMode(
     jvmti_env, JVMTI_ENABLE, JVMTI_EVENT_GARBAGE_COLLECTION_START, (jthread) NULL); 

    if (returnCode != JNI_OK) 
    { 
     fprintf(
      stderr, 
      "C:\tJVM does not have the required capabilities, JVMTI_ENABLE, JVMTI_EVENT_GARBAGE_COLLECTION_START (%d)\n", 
      returnCode); 
     exit(-1); 
    } 


    returnCode = (*jvmti_env)->SetEventNotificationMode(
     jvmti_env, JVMTI_ENABLE, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, (jthread) NULL); 

    if (returnCode != JNI_OK) 
    { 
     fprintf(
      stderr, 
      "C:\tJVM does not have the required capabilities, JVMTI_ENABLE, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH (%d)\n", 
      returnCode); 
     exit(-1); 
    } 


    if(requiredCapabilities) free(requiredCapabilities); 
    if(eventCallbacks) free(eventCallbacks); 

    return JVMTI_ERROR_NONE; 
} 
1

Về -Xloggc: Tính cập nhật jdk1.6 4 bạn có thể lấy Sun/Oracle JVM để in ra ngày tháng và thời gian sử dụng -XX:+PrintGCDateStamps. Điều này làm cho các bản ghi nhiều hơn hữu ích hơn, đặc biệt nếu bạn thêm máy quét/màn hình nhật ký có thể thông báo cho bạn về bất kỳ vấn đề GC nào.

2

Một trường hợp sử dụng cho việc sắp xảy ra thông báo GC: nếu ứng dụng của bạn là cân bằng tải sau đó thì bạn có thể thông báo cho cân bằng tải để đưa nút của bạn ra khỏi hồ bơi khi một GC sắp bắt đầu để nó không nhận được yêu cầu mà sẽ phải chờ một GC đầy đủ để được xử lý.

Điều đó không giúp các yêu cầu trong chuyến bay đến trước khi GC khởi động nhưng trong trường hợp của tôi ít nhất, hầu hết các yêu cầu đều là giây và các GC chính là 5-10s, cứ sau vài phút. Chúng ta có thể tinh chỉnh tỷ lệ NewGen vv nhưng điểm chung vẫn được áp dụng: GC chính có thể dài hơn nhiều so với thời gian phản hồi điển hình, vì vậy bạn có thể muốn premptively dừng một nút bắt đầu từ GC chính.

Khi GC kết thúc, một luồng trong JVM có thể gửi thông báo đến bộ cân bằng tải để cho phép nó biết về hoạt động kinh doanh hoặc LB có thể dựa vào sự cân bằng thông thường của nó.

2

Tôi biết điều này là rất muộn nhưng tôi hy vọng nó có thể giúp ai đó một ngày nào đó.

Bạn có thể nhận các sự kiện như vậy bằng cách sử dụng thư viện mà tôi đang phát triển được gọi là gcRadar. Nó cung cấp thông tin về chính xác khi một đối tượng được thu gom rác thải.

Mọi đề xuất cải tiến trong thư viện đều được hoan nghênh.

4

Java mẫu mã sử dụng GarbageCollectorMXBean đề cập đến trong các câu trả lời được chấp nhận:

static 
{ 
    // notification listener. is notified whenever a gc finishes. 
    NotificationListener notificationListener = new NotificationListener() 
    { 
     @Override 
     public void handleNotification(Notification notification,Object handback) 
     { 
      if (notification.getType().equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION)) 
      { 
       // extract garbage collection information from notification. 
       GarbageCollectionNotificationInfo gcInfo = GarbageCollectionNotificationInfo.from((CompositeData) notification.getUserData()); 

       // access garbage collection information... 
      } 
     } 
    }; 

    // register our listener with all gc beans 
    for (GarbageCollectorMXBean gcBean : ManagementFactory.getGarbageCollectorMXBeans()) 
    { 
     NotificationEmitter emitter = (NotificationEmitter) gcBean; 
     emitter.addNotificationListener(notificationListener,null,null); 
    } 
} 

site that has detailed sample code that uses the GarbageCollectorMXBean.

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