コールバックをJVMに登録して、ガベージコレクションがいつ発生するかを確認したいと思います。これを行う方法はありますか?
編集:これを実行して、アプリケーションログでガベージコレクションが発生したときにログアウトできるようにします。これにより、発生している問題と相関関係があるかどうかを確認できます。 -Xloggcをオンにすると便利ですが、GCログ(アプリの起動から数秒を使用)の時間をメインのアプリケーションログに統合するのは少し注意が必要です。
2012年4月の編集:Java7u4の時点で、GarbageCollectorMXBeanから通知を受け取ることができます(ニース 例 )。
Java7u4以降、GarbageCollectorMXBeanから通知を受け取ることができます。 http://docs.Oracle.com/javase/7/docs/jre/api/management/extension/com/Sun/management/GarbageCollectionNotificationInfo.html を参照してください。
標準的な方法は、 JVMツールインターフェイス(JVM TI) を使用して、GC開始コールバックを使用してエージェントを作成し、そこから時間をログに記録することだと思います( GetTime を参照)。 )。 ガベージコレクション開始イベント は完全なGCに対してのみ送信されることに注意してください。
サンプルのJVMTIエージェントは、JDK5.0またはJDK6ダウンロードのデモディレクトリにあります。技術記事 JVMツールインターフェイス(JVM TI):How VM Agents Work は、もう1つの非常に優れたリソースです。 デバッグの作成)も参照してください。およびJVMTIを使用したプロファイリングエージェント 。
受け入れられた回答で参照されているGarbageCollectorMXBean
を使用したJavaコードサンプル:
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);
}
}
MemoryPoolMXBeanを使用して、コレクション使用量のしきい値を1に設定できるようです。これにより、gcが実行され、少なくとも1バイトのメモリが使用されているときに通知が表示されます。
http://Java.Sun.com/j2se/1.5.0/docs/api/Java/lang/management/MemoryPoolMXBean.html
ただし、これはすべてのガベージコレクタで機能するわけではないようです。
私はこれが非常に遅いことを知っていますが、いつか誰かに役立つことを願っています。
私が開発している gcRadar というライブラリを使用することで、このようなイベントを受け取ることができます。オブジェクトがガベージコレクションされた正確な時期に関する情報を提供します。
ライブラリの改善に関する提案は大歓迎です。
ガベージコレクションのJVMTIイベントを受信すると、JVMは技術的に停止するため、JNI経由でJavaリスナーをコールバックできません。..このエージェントは、GCの開始時と終了時の時刻を出力します。 SunJVMの詳細なGCよりも高い解像度。
#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;
}
差し迫ったGC通知を取得するための別のユースケース:アプリが負荷分散されている場合、GCが開始しようとしているときに、待機する必要のあるリクエストを受信しないように、ロードバランサーにノードをプールから削除するよう通知できます。完全なGCを処理するために。
これは、GCが開始される直前に到着した処理中のリクエストには役立ちませんが、少なくとも私の場合、ほとんどのリクエストは1秒未満で、主要なGCは数分ごとに5〜10秒です。 NewGenの比率などを微調整することはできますが、一般的なポイントは引き続き適用されます。メジャーGCは通常の応答時間よりもはるかに長い可能性があるため、先制的に停止することをお勧めします。要求の受信からメジャーGCを開始するノード。
GCが終了すると、JVMのスレッドはロードバランサーに通知を送信して、ビジネスに戻ったことを通知するか、LBは通常のキープアライブに依存できます。
独自のプログラムがガベージコレクションに関する情報をJVMから取得するための標準的な方法はありません。このようなAPIはベンダー固有です。
なぜあなたが見つけた施設が不十分なのですか?
-Xloggc
について:jdk1.6 update 4以降、-XX:+PrintGCDateStamps
を使用してSun/OracleJVMに日付と時刻を出力させることができます。これにより、ログmuchがより便利になります。特に、GCの問題を通知できるログスキャナー/モニターを追加する場合に役立ちます。
Javalobbyに関する興味深い記事 これを行う1つの方法について説明しています。
これを診断ツールとして見ている場合は、アプリケーションログをStdOutにリダイレクトしてから、StdOutとStdErrの両方をファイルにリダイレクトすることをお勧めします。これにより、アプリケーションコードを変更しなくても、JVMロギングの詳細がわかります。