Я отлаживал высокую загрузку системного процессора (не использование пользовательского процессора) наших бурных супервизоров (Wheezy machine). Вот наблюдения
Вывод perf для соответствующего процесса:
Events: 10K cpu-clock
16.40% java [kernel.kallsyms] [k] system_call_after_swapgs
13.95% java [kernel.kallsyms] [k] pvclock_clocksource_read
12.76% java [kernel.kallsyms] [k] do_gettimeofday
12.61% java [vdso] [.] 0x7ffe0fea898f
9.02% java perf-17609.map [.] 0x7fcabb8b85dc
7.16% java [kernel.kallsyms] [k] copy_user_enhanced_fast_string
4.97% java [kernel.kallsyms] [k] native_read_tsc
2.88% java [kernel.kallsyms] [k] sys_gettimeofday
2.82% java libjvm.so [.] os::javaTimeMillis()
2.39% java [kernel.kallsyms] [k] arch_local_irq_restore
Поймал это в цепочке потока соответствующего процесса
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
100.00 0.000247 0 64038 gettimeofday
0.00 0.000000 0 1 rt_sigreturn
0.00 0.000000 0 1 futex
------ ----------- ----------- --------- --------- ----------------
100.00 0.000247 64040 total
Наконец выяснилось, что поток работал в while(true)
, а один из вызовов внутри - System.currentTimeMillis()
. Я отключил его, а системный CPU% снизился с 50% до 3%. Так ясно, что это была проблема. Я не понимаю, что при наличии vDSO эти вызовы ядра должны происходить только в адресном пространстве пользователя. Но, как видно из перфорированного отчета, вызовы ядра действительно происходят в пространстве ядра. Любые указатели на это?
Версия ядра: 3.2.0-4-amd64 Debian 3.2.86-1 x86_64 GNU/Linux
Тип часов: kvm
Добавление кода проблемной нити.
@RequiredArgsConstructor
public class TestThread implements Runnable {
private final Queue<String> queue;
private final Publisher publisher;
private final int maxBatchSize;
private long lastPushTime;
@Override
public void run() {
lastPushTime = System.currentTimeMillis();
List<String> events = new ArrayList<>();
while (true) {
try {
String message = queue.poll();
long lastPollTime = System.currentTimeMillis();
if (message != null) {
events.add(message);
pushEvents(events, false);
}
// if event threshold hasn't reached the size, but it been there for over 10seconds, push it.
if ((lastPollTime - lastPushTime > 10000) && (events.size() > 0)) {
pushEvents(events, true);
}
} catch (Exception e) {
// Log and do something
}
}
}
private void pushEvents(List<String> events, boolean forcePush) {
if (events.size() >= maxBatchSize || forcePush) {
pushToHTTPEndPoint(events);
events.clear();
lastPushTime = System.currentTimeMillis();
}
}
private void pushToHTTPEndPoint(List<String> events) {
publisher.publish(events);
}
}