Почему бы сразу уничтоженный общий указатель утечки памяти? - PullRequest
1 голос
/ 22 февраля 2012

Есть ли здесь утечка памяти?

class myclass : public boost::enable_shared_from_this<myclass>{
//...
    void broadcast(const char *buf){
    broadcast(new std::string(buf));
    }

    void broadcast(std::string *buf){
    boost::shared_ptr<std::string> msg(buf);
    }
//...
};  

(Это урезанная версия, которая все еще показывает проблему - обычно я выполняю реальную работу во время этого второго broadcast вызова!) Я предполагаю, чтопервый вызов получает немного памяти, затем, поскольку я ничего не делаю с умным указателем, второй вызов немедленно удалит его.Просто?Но когда я запускаю свою программу, память увеличивается со временем, в прыжках.Тем не менее, когда я закомментирую единственный вызов в программе для broadcast (), он не будет!

Вывод ps для версии без broadcast():

 %CPU  %MEM     VSZ    RSS  TIME
 3.2   0.0     158068  1988 0:00 
 3.3   0.0     158068  1988 0:25  (12 mins later)

При вызовеbroadcast() (в Ubuntu 10.04, g ++ 4.4, boost 1.40)

 %CPU  %MEM     VSZ    RSS  TIME
 1.0    0.0    158068  1980 0:00
 3.3    0.0    158068  1988 0:04  (2 mins)
 3.4    0.0    223604  1996 0:06  (3.5 mins)
 3.3    0.0    223604  2000 0:09
 3.1    0.0    223604  2000 2:21  (82 mins)
 3.1    0.0    223604  2000 3:50  (120 mins)

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

При вызове broadcast() (в Centos 5.6, g ++ 4.1, boost 1.41)

 %CPU  %MEM     VSZ    RSS  TIME
 0.0    0.0     51224  1744 0:00
 0.0    0.0     51224  1744 0:00  (30s)
 1.1    0.0    182296  1776 0:02  (3.5 mins)
 0.7    0.0    182296  1776 0:03
 0.7    0.0    182296  1776 0:09  (20 mins)
 0.7    0.0    247832  1788 0:14  (34 mins)
 0.7    0.0    247832  1788 0:17
 0.7    0.0    247832  1788 0:24  (55 mins)
 0.7    0.0    247832  1788 0:36  (71 mins)

Вот как вызывается broadcast() (из таймера boost :: asio), и теперь я 'Мне интересно, может ли это иметь значение:

void callback(){
//...
timer.expires_from_now(boost::posix_time::milliseconds(20));
//...
char buf[512];
sprintf(buf,"...");
broadcast(buf);
timer.async_wait(boost::bind(&myclass::callback, shared_from_this() ));
//...
}

(обратный вызов находится в том же классе, что и функция широковещания)

У меня 4 таких таймера, и мой io_service.run() вызываетсяпулом из 3-х потоков.Мой тайм-аут 20 мс означает, что каждый таймер вызывает broadcast() 50 раз в секунду.Я устанавливаю срок действия в начале своей функции и запускаю таймер ближе к концу.Проверенный код не так уж много делает;вывод отладочной информации в std :: cout, пожалуй, самая трудоемкая работа.Я полагаю, что это может быть возможно, таймер срабатывает сразу же иногда;но, тем не менее, я не могу понять, как это может быть проблемой, не говоря уже о том, чтобы вызвать утечку памяти.

(Кстати, программа работает нормально, даже когда выполняет свои полные задачи; у меня возникли подозрения только тогда, когда язаметил, что использование памяти, о котором сообщает ps, увеличилось.)

ОБНОВЛЕНИЕ: Спасибо за ответы и комментарии.Могу добавить, что я оставил программу работающей на каждой системе еще на пару часов, и использование памяти больше не увеличивалось.(Я также был готов отклонить это как одноразовую реструктуризацию кучи или что-то подобное, когда версия Centos подскочила во второй раз.) В любом случае, приятно знать, что мое понимание умных указателей все еще здраво, и что естьнет странного углового случая с потоками, о котором я должен беспокоиться.

Ответы [ 2 ]

2 голосов
/ 22 февраля 2012

В случае утечки вы выделяете std::string (20 байт, более или менее) 50 раз в секунду. через 1 час вы должны были быть выделены ... 3600 * 50 * 20 = 3,4MBytes.

Ничего общего с 64K вы не видите, это, вероятно, связано с тем, как система выделяет память для процесса, который new перераспределяет переменные.

Система, когда что-то удаляется, должна «собрать мусор», поместив его обратно в доступную цепочку памяти для дальнейшего распределения. Но так как это занимает много времени, большинство систем не выполняют эту операцию до тех пор, пока объем свободной памяти не превысит определенных значений, поэтому можно выполнить «перепаковку».

1 голос
/ 22 февраля 2012

Хорошо, что здесь происходит, вероятно, не из-за утечки вашей программы, а из-за того, что по какой-то причине распределитель системной памяти решил сохранить еще одну страницу размером 64 КБ для вашего приложения. Если бы в этой точке была постоянная утечка памяти с частотой 50 Гц, это имело бы гораздо более драматический эффект!

Точно, почему это делается через 3 минуты, я не знаю (я не эксперт в этой области), но я предполагаю, что в этом есть некоторая эвристика и статистика. Или это может быть просто фрагментарность кучи.

Другая вещь, которая могла произойти, это то, что сообщения, которые вы держите в буфере, со временем становятся длиннее:)

...