Конкуренция в гарбэдж коллекторе (GC_CONCURRENNT)

Ответить
Nyashka
Сообщения: 40
Зарегистрирован: 19 мар 2013, 13:06

Конкуренция в гарбэдж коллекторе (GC_CONCURRENNT)

Сообщение Nyashka » 21 сен 2014, 13:20

В процессе переделок заметил что производительность моего рантайм приложения существенно понизилась, и как показывает сравнительный анализ с предыдущими версиями, во многом из за существенно возросшего числа вызовов GC_CONCURRENT останавливающего приложение на 50-100 мс что чрезвычайно разрушительно для рантайма. Погрешив на увеличение количества потоков я переписал приложение так что бы оно все делало в основном потоке, однако, количество вызовов GC_CONCURRENT это не уменьшило ни сколько.
Отсюда вопрос: о какой конкуренции идет речь?
Пока на уме только копирование заимствованного из других классов кода, что избежать ни как не получится

Аватара пользователя
Mikhail_dev
Сообщения: 2386
Зарегистрирован: 09 янв 2012, 14:45
Откуда: Самара

Re: Конкуренция в гарбэдж коллекторе (GC_CONCURRENNT)

Сообщение Mikhail_dev » 21 сен 2014, 13:43

Почитайте данную статью - http://habrahabr.ru/post/222199/ , особенно Allocation tracker и Method profiling. В первом вы найдете такую информацию, как какие объекты создаются часто (ведь GC их и чистит), а во втором можете посмотреть какие места программы у вас самые нагруженные.
Погрешив на увеличение количества потоков я переписал приложение
Хех, не стоило этого делать. В таких ситуациях легче знать, а не думать.

Аватара пользователя
altwin
Сообщения: 1951
Зарегистрирован: 13 ноя 2013, 14:46

Re: Конкуренция в гарбэдж коллекторе (GC_CONCURRENNT)

Сообщение altwin » 22 сен 2014, 09:14

Когда -то давно подобная тема подымалась, потому просто оставлю цитату одного ответа:
Что же, проблема решилась. С памятью и количеством объектов на java это оказалось никак не связано.
Тормозил вовсе не GC_CONCURRENT. В глубине далвика есть такая функция - gcDaemonThread. Это демон сборщика мусора. Как гласит её описание, она инициирует GC_CONCURRENT по внешнему сигналу. Так же через несколько секунд после этого она обрезает кучу. Из-за бага ( https://code.google.com/p/android/issue ... l?id=26627 ) (который есть по меньшей мере в 4.0.3) обрезка кучи происходила не через несколько секунд, а ровно через 5 мсек после сборки мусора. Т.е. визуально дерганье происходило ровно в момент появления в логах сообщения о работе сборщика. Поэтому сразу начали грешить на невиновный, как оказалось, GC_CONCURRENT.

Функция обрезки кучи trimHeaps() сканирует память процесса на предмет свободных страниц памяти, и когда находит, вызывает системную функцию madvise(..., MADV_DONTNEED). Всё бы ничего, но на время работы этой функции лочится далвиковская куча. Т.е. если вдруг в этот момент в java вызывается new, то он послушно ждет, пока trimHeaps() закончит. В особо тяжелых случаях время её работы у меня достигало 300-400 мсек. Для игры такая пауза определенно бросалась в глаза.

Внутри функции trimHeaps() вызывалась функция dlmalloc_walk_free_pages(). Именно она из 300 мсек работала 290. От чего зависит время её работы? От количества объектов, созданных в нативном коде(имеется ввиду код на С++, скомпилированный с помощю ndk). Конкретно в моем случае плодило в больших количествах объекты бинарное дерево. В некоторых ситуациях количество нод - отдельно созданных объектов - достигало 100к и больше. И хотя одна нода занимала всего 16 байт, а всё дерево чуть больше полутора мегабайт, количество произведенных аллокаций катастрофическим образом сказывалось на времени работы dlmalloc_walk_free_pages().

Как решение проблемы, написал промежуточный аллокатор, который обслуживает бинарное дерево. В результате количество прямых dlmalloc уменьшилось на 3 порядка. dlmalloc_walk_free_pages() теперь не привлекает внимания. Все рады.

Вывод:
Количество аллокаций в нативном коде может серьезно затормозить аллокации на java-стороне, хотя раньше я думал что в нативном коде могу творить что хочу. Будьте осторожны.
Изображение

Аватара пользователя
Mikhail_dev
Сообщения: 2386
Зарегистрирован: 09 янв 2012, 14:45
Откуда: Самара

Re: Конкуренция в гарбэдж коллекторе (GC_CONCURRENNT)

Сообщение Mikhail_dev » 22 сен 2014, 10:02

Хм, интересная заметка.

Но это можно легко проверить. Достаточно лишь проанализировать тулзой "Method profiling' кто и как долго поедает. Если это будет GC, то будет большой процент его работы.

Nyashka
Сообщения: 40
Зарегистрирован: 19 мар 2013, 13:06

Re: Конкуренция в гарбэдж коллекторе (GC_CONCURRENNT)

Сообщение Nyashka » 22 сен 2014, 10:37

altwin писал(а):Когда -то давно подобная тема подымалась, потому просто оставлю цитату одного ответа:
Тоже натыкался на эту статью когда пытался загуглить вопрос, но очевидно что к падению производительности имели именно мои действия.
Mikhail_dev писал(а):Почитайте данную статью - http://habrahabr.ru/post/222199/ , особенно Allocation tracker и Method profiling. В первом вы найдете такую информацию, как какие объекты создаются часто (ведь GC их и чистит), а во втором можете посмотреть какие места программы у вас самые нагруженные.
Цитата:
Погрешив на увеличение количества потоков я переписал приложение

Хех, не стоило этого делать. В таких ситуациях легче знать, а не думать.
Как показал анализ DDMS проблема скрылась в инициализации такой простой вещи как вектор (двухколесный), и алгоритма работающего на большом числе операций с векторами, среди которых встречается умножение на число, возвращающее новый вектор. Это было не страшно до того момента как я расширил вектор внеся в него список линкованных данных, и с тех пор цена создания вектора существенно возросла. Нынешнее состояние жрет 13% процессорного времени только на создание этих самых векторов

Аватара пользователя
Mikhail_dev
Сообщения: 2386
Зарегистрирован: 09 янв 2012, 14:45
Откуда: Самара

Re: Конкуренция в гарбэдж коллекторе (GC_CONCURRENNT)

Сообщение Mikhail_dev » 22 сен 2014, 11:05

но очевидно что к падению производительности имели именно мои действия.
Ну это понятно. Просто так можно "в лоб" найти узкие места производительности.

Ответить