DTrace: осторожно с глобальными переменными В DTrace есть 3 вида переменных:
- глобальные (global)—доступны везде.
- локальные в рамках потока (thread-local)—переменные
определяются в области, локальной для каждого потока ОС и как следствие
доступны в пробах, срабатываемых в рамках этих потоков. Доступ к этим
переменным осуществляется с помощью префикса
self->. - локальные в рамках пробы (clause-local)—переменные доступны только в
рамках определенных действий для конкретной пробы.
В каком-то смысле эти переменные напоминают статические переменные в
языке C, определенные в рамках функции.
Доступ к этим переменным осуществляется с помощью префикса
this->.
В общем-то из названий переменных сценарии использования различных типов переменных вполне очевидны. Хочется обратить внимание на то, что глобальные переменные не являются MP-safe (их использование небезопасно в многопроцессорных системах). Т.е., если проба срабатывает на нескольких процессорах одновременно (в свою очередь в рамках различных потоков) и в рамках действия для этой пробы увеличивается значение некой глобальной переменной, то атомарность этих действий не гарантируется и, как следствие, значение переменной может оказаться неверным.
Для демонстрации рассмотрим пример с использванием пробы
hotspot:::object-alloc.
Напишем небольшую Java програму (ObjectAllocator.java), которая будет создавать
100.000 объектов
типа ObjectToAlloc в 10 параллельных потоках:
class ObjectToAlloc { }
class ObjectAllocatorThread extends Thread {
static final int OBJECTS_CNT = 10000;
ObjectAllocatorThread(String name) {
super(name);
}
public void run() {
// создаем объекты
for (int i=0; i < OBJECTS_CNT; i++)
new ObjectToAlloc();
}
}
public class ObjectAllocator {
static final int THREADS_CNT = 10;
static public void main(String[] args) throws Exception {
ObjectAllocatorThread[] threads = new ObjectAllocatorThread[THREADS_CNT];
// создаем потоки
for (int i=0; i < THREADS_CNT; i++)
threads[i] = new ObjectAllocatorThread("ObjectAllocatorThread_" + i);
// запускаем потоки
for (int i=0; i < THREADS_CNT; i++)
threads[i].start();
// ждем завершения потоков
for (int i=0; i < THREADS_CNT; i++)
threads[i].join();
}
}
Теперь напишем D-скрипт object_allocation_stat.d, который будет считать сколько объектов типа
ObjectToAlloc было создано в рамках Java программы.
#!/usr/sbin/dtrace -ZsДля демонстрации некоректности значения глобальной переменной
#pragma D option quiet
long long ALLOCATED_OBJECTS_CNT;
hotspot$target:::object-alloc
{
self->str_ptr = (char*) copyin(arg1, arg2+1);
self->str_ptr[arg2] = '\0';
self->class_name = (string) self->str_ptr;
}
hotspot$target:::object-alloc
/ self->class_name == "ObjectToAlloc" /
{
ALLOCATED_OBJECTS_CNT ++;
@allocs_count[tid] = count();
}
:::END
{
printf("%10s %10s\n", "thread", "objects");
printa("%10d %10@d\n", @allocs_count);
printf("\nALLOCATED_OBJECTS_CNT: %d\n", ALLOCATED_OBJECTS_CNT);
}
ALLOCATED_OBJECTS_CNT отдельно считаем количество объектов, созданных
в рамках конкретного потока и записываем значение в
ассоциативный массив
allocs_count (ключ = идентификатор потока).
Компилируем ObjectAllocator.java и делаем object_allocation_stat.d
исполняемым.
% $JDK6_HOME/javac ObjectAllocator.java
% chmod +x object_allocation_stat.d
Запускаем object_allocation_stat.d и видим, что значение глобальной
переменной ALLOCATED_OBJECTS_CNT совсем не 100000.
% ./object_allocation_stat.d -c "$JDK6_HOME/java -XX:+ExtendedDTraceProbes ObjectAllocator"
thread objects
13 10000
14 10000
15 10000
16 10000
17 10000
18 10000
19 10000
20 10000
21 10000
22 10000
ALLOCATED_OBJECTS_CNT: 94007
Ошибка? Нет.
Со слов автора DTrace, реализовать корректную поддержку глобальных переменных DTrace в много-процессорном окружении практически невозможно. По-видимому, дело в том, что такая реализация скажется на производительности даже если DTrace не используется.
Так что будьте внимательны при использовании глобальных переменных!
Е. П. опубликовал vmrobot ( янв 16 2007, 06:09:29 PM MSK ) Permalink Комментарии [0]
