Подтвердить что ты не робот

Уведомление о сборе мусора?

Я хотел бы зарегистрировать обратный вызов с JVM, чтобы я знал, когда происходит сбор мусора. Есть ли способ сделать это?

EDIT: Я хочу сделать это, чтобы я мог выйти из системы, когда сбор мусора происходит в моем журнале приложений, поэтому я могу видеть, коррелирует ли это с проблемами, которые я вижу. Включение -Xloggc полезно, но немного сложнее интегрировать времена из журнала GC (которые используют секунды с момента запуска приложения) в мой основной журнал приложений.

EDIT Апрель 2012: Начиная с Java7u4, вы можете получать уведомления от GarbageCollectorMXBean (хороший пример).

4b9b3361

Ответ 2

Я думаю, что стандартным способом является использование JVM Tool Interface (JVM TI) для записи агента с обратным вызовом GC start и запишите время из него (см. GetTime). Обратите внимание, что Событие запуска коллекции мусора отправляется только для полных GC.

Примеры агентов JVM TI доступны в демо-каталоге JDK 5.0 или загрузки JDK 6. Техническая статья Интерфейс JVM Tool (JVM TI): как работают агенты VM - еще один очень ресурс. Также посмотрите Создание агента отладки и профилирования с помощью JVMTI.

Ответ 3

Похоже, вы можете использовать MemoryPoolMXBean и установить порог использования коллекции на 1. Это должно дать вам уведомление в любое время, когда gc запускается, и по-прежнему имеется по крайней мере байт используемой памяти.

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

Похоже, что это не работает со всеми сборщиками мусора.

Ответ 4

Пример кода Java, используя GarbageCollectorMXBean, о котором говорится в принятом ответе:

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);
    }
}

сайт с подробным примером кода, который использует GarbageCollectorMXBean.

Ответ 5

При получении события JVMTI для Garbage Collection JVM технически прекращается, поэтому он не может перезвонить слушателю Java через JNI.... этот агент печатает время при запуске и завершении GC с более высоким разрешением, чем подробный GC на 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;
}

Ответ 6

Еще один пример использования для получения ожидающих уведомлений GC: если ваше приложение сбалансировано по нагрузке, тогда вы можете затем уведомить балансировщик нагрузки о том, чтобы вывести ваш node из пула, когда GC собирается начать с того, что он не получает запросы что придется ждать полного GC, с которым можно будет справиться.

Это не помогает запросам в полете, которые прибыли непосредственно перед тем, как GC взорвался, но, по крайней мере, в моем случае, большинство запросов являются субсекундами, а основные GC - 5-10 секунд, каждые несколько минут. Мы можем настроить коэффициенты NewGen и т.д., Но общий момент по-прежнему применяется: основной GC может быть намного длиннее обычного времени отклика, поэтому вы можете предварительно остановить node, начиная основной GC от получения запросов.

Когда GC завершен, поток в JVM может отправить уведомление балансировщику нагрузки, чтобы он знал его обратно в бизнесе, или LB может полагаться на свой обычный keepalive.

Ответ 7

Я знаю, что это очень поздно, но я надеюсь, что это когда-нибудь поможет кому-нибудь.

Вы можете получить такие события с помощью использования библиотеки, которую я разрабатываю, называемой gcRadar. Он предоставляет информацию о том, когда именно объект был собран мусором.

Любые предложения по улучшению в библиотеке приветствуются.

Ответ 9

Нет стандартного способа для вашей собственной программы получать информацию от JVM о сборе мусора. Любой такой API специфичен для поставщика.

Почему средство, которое вы обнаружили недостаточно?

Ответ 10

Относительно -Xloggc: Начиная с версии 4 jdk1.6 вы можете заставить Sun/Oracle JVM распечатать дату и время с помощью -XX:+PrintGCDateStamps. Это делает журналы намного более полезными, особенно если вы добавляете сканер/монитор журнала, который может уведомить вас о любых проблемах с GC.

Ответ 11

Если вы рассматриваете это как инструмент диагностики, я рекомендую перенаправить ваш журнал приложений в StdOut, а затем перенаправить StdOut и StdErr в файл. Это даст вам подробную информацию о регистрации JVM, не заставляя вас менять код приложения.