Для изучения того, что происходит с Java-программой в операционной системе, в JVM начиная с версии 6.0 есть датчики (probes) двух провайдеров – hotspot и hotspot_jni.
Датчики провайдера hotspot
VM Lifecycle Probes (жизненный цикл VM)
vm-init-begin - момент начала инициализации,
vm-init-end - момент окончания инициализации,
vm-shutdown - момент завершения.
Thread Lifecycle Probes (жизненный цикл Java-потоков)
thread-start – момент запуска потока,
thread-stop - момент остановки потока.
Classloading Probes (загрузчик классов)
class-loaded - момент завершения загрузки класса,
class-unloaded - момент завершения выгрузки класса.
GC Probes (сборщик мусора)
gc-begin - момент начала работы сборщика мусора,
gc-end - момент завершения работы сборщика мусора,
mem-pool-gc-begin - момент начала сборки мусора или очистки данного коллектора,
mem-pool-gc-end - момент окончания сборки мусора или очистки коллектора.
Method Compilation Probes (компиляция Java-методов JIT-компилятором)
method-compile-begin - момент начала компиляции метода,
method-compile-end - момент окончания компиляции метода,
compiled-method-load - момент начала загрузки скомпилированного метода в VM,
compiled-method-unload - момент окончания загрузки скомпилированного метода в VM.
Monitor Probes (Java-мониторы)
monitor-contended-enter - момент начала захвата монитора, захваченного другим потоком,
monitor-contended-exit - момент освобождения потоком монитора, пытающегося захватить другой поток,
monitor-wait - момент, когда поток входит в состояние ожидания в результате вызова Object.wait(),
monitor-waited - момент завершения ожидания Object.wait() по истечении времени ожидания (timeout) или получения уведомления от другого потока с помощью Object.notify() или Object.notifyAll(),
monitor-notify - момент вызова Object.notify(),
monitor-notifyAll - момент вызова Object.notifyAll().
Application Tracking probes (работа приложения)
method-entry – момент входа в Java-метод,
method-exit – момент выхода из Java-метода,
object-alloc - момент создания Java-объекта.
Пример работы скрипта, отслеживающего работу сборщика мусора:
#java -jar /muCommander-0_8_2/mucommander.jar
#dtrace -qn 'hotspot$target:::gc-begin
>
> {
>
> printf("GC called at %Y\n", walltimestamp);
>
> }
>hotspot$target:::gc-end
>
>{
>
>printf("GC finished at %Y\n", walltimestamp);
>
>}' -p 722
GC started at 2008 Jul 13 22:40:01
GC finished at 2008 Jul 13 22:40:31
GC started at 2008 Jul 13 22:41:01
GC finished at 2008 Jul 13 22:41:31
GC started at 2008 Jul 13 22:42:01
GC finished at 2008 Jul 13 22:42:31
Выражение hotspot$target означает, что вместо $target будет подставлен аргумент, переданный с ключом -p при запуске dtrace (PID Java-приложения).
По умолчанию в JVM доступны датчики, незначительно влияющие на производительность - отслеживающие сборку мусора, компиляцию методов, создание нового потока команд и загрузку классов.
Чтобы сделать доступными датчики создания объектов, вызова методов и датчики событий, связанных с мониторами Java, нужно запустить приложение c ключами, разрешающими использование этих датчиков или, если JVM уже запущена, использовать команду jinfo для управления JVM.
Используемые ключи:
-XX:+DTraceAllocProbes
-XX:+DTraceMethodProbes
-XX:+DTraceMonitorProbes
-XX:+ExtendedDTraceProbes
Четвертый ключ разрешает использование всех датчиков JVM.
Примеры работы скриптов для определения методов, вызываемых в Java-приложении:
hs.d
hotspot$target:::method-entry
{
printf("%s.%s %s\n",copyinstr(arg1,arg2),copyinstr(arg3,arg4),
copyinstr(arg5,arg6));
exit(0);
}
# java -XX:+ExtendedDTraceProbes /muCommander-0_8_2/mucommander.jar
#dtrace -qs hs.d -p 710
sun/awt/SunToolkit.\awtLock ()V
sun/awt/SunToolkit.awtLock
<…>
meth.d
hotspot$target:::method-entry
{
this->str = strjoin(".", copyinstr(arg3, arg4));
@[strjoin(copyinstr(arg1, arg2), this->str)] = count();
}
#dtrace -qs meth.d -p 710
java/util/Vector.\size ()I
<…>
Трассировка cтека вызовов программы с использованием метода RangeCheck:
range.d
hotspot$target:::method-entry
/copyinstr(arg3, arg4) == "RangeCheck"/
{
@[jstack(30, 16384)] = count();
exit(0);
}
# dtrace -qs range.d -p 756
libjvm.so`1cNSharedRuntimeTdtrace_method_entry6FpnKJavaThread_pnNmethodOopDesc__i_+0x7b
java/util/ArrayList.RangeCheck(I)V*
java/util/ArrayList.get(I)Ljava/lang/Object;*
java/util/AbstractList$Itr.next()Ljava/lang/Object;*
sun/awt/X11/XToolkit.callTimeoutTasks()V*
sun/awt/X11/XToolkit.run(Z)V
sun/awt/X11/XToolkit.run()V
java/lang/Thread.run()V
StubRoutines (1)
libjvm.so`1cJJavaCallsLcall_helper6FpnJJavaValue_pnMmethodHandle_pnRJavaCallArguments_pnGThread__v_+0x1a3
libjvm.so`1cCosUos_exception_wrapper6FpFpnJJavaValue_pnMmethodHandle_pnRJavaCallArguments_pnGThread__v2468_v_+0x27
libjvm.so`1cJJavaCallsEcall6FpnJJavaValue_nMmethodHandle_pnRJavaCallArguments_pnGThread__v_+0x2f
libjvm.so`1cJJavaCallsMcall_virtual6FpnJJavaValue_nLKlassHandle_nMsymbolHandle_4pnRJavaCallArguments_pnGThread__v_+0xc1
libjvm.so`1cJJavaCallsMcall_virtual6FpnJJavaValue_nGHandle_nLKlassHandle_nMsymbolHandle_5pnGThread__v_+0x7e
libjvm.so`1cMthread_entry6FpnKJavaThread_pnGThread__v_+0xd2
libjvm.so`1cKJavaThreadRthread_main_inner6M_v_+0x4c
libjvm.so`1cKJavaThreadDrun6M_v_+0x182
libjvm.so`java_start+0xee
libc.so.1`thr_setup+0x52
libc.so.1`lwp_start
1
Для автоматического завершения скрипта через
n миллисекунд используется конструкция
}
tick-nms
{
Используемые аргументы датчика method-entry:
args[0]- идентификатор потока команд, вызывающего метод,
args[1]- имя класса метода (указатель на строку),
args[2]- длина имени класса метода (в байтах),
args[3]- имя метода (указатель на строку),
args[4]- длина имени метода (в байтах),
args[5]- сигнатура метода (указатель на строку),
args[6]- длина сигнатуры метода (в байтах).
Сигнатура метода – совокупность имени функции, типа возвращаемого значения и списка аргументов с указанием порядка их следования и типов.
Провайдер hotspot_jni
Этот провайдер предоставляет DTrace набор датчиков входа и выхода каждого JNI-метода и требуется для отслеживания событий, связанных с обращениями через JNI (Java native interface - интерфейс между программой на Java и машинным кодом).
Для каждого JNI-метода имеется пара проб:
hotspot_jni$pid:::JNIMETHODNAME-entry
и
hotspot_jni:::JNIMETHODNAME-return.
Скрипт, считающий статистику вызовов методов JNI:
jni.d
hotspot_jni$target:::*-entry
{
JNI_CALLS ++;
@jni_calls[probename] = count();
}
:::END
{
printa("%10@d %s\n", @jni_calls);
printf("\n");
printf("Total number of JNI calls: %d\n", JNI_CALLS);
exit(0);
}
#java -jar /muCommander-0_8_2/mucommander.jar
#dtrace -s jni.d -p 710
dtrace: script 'jni.d' matched 238 probes
<…>
Если добавить строки
%cat script_name.d
#!/usr/sbin/dtrace –s
и сделать
скрипты исполняемыми (chmod +x script_name.d), можно запускать их в командной строке, передавая в качестве аргумента идентификатор процесса:
./script_name.d -p PID