Форум программистов, компьютерный форум, киберфорум
C/С++ под Linux
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.69/13: Рейтинг темы: голосов - 13, средняя оценка - 4.69
0 / 0 / 0
Регистрация: 02.02.2016
Сообщений: 14

Не освобождается память std::string после использования std::bind

06.12.2019, 13:28. Показов 2822. Ответов 27
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Всем привет!

Есть система, которая подгружает из внешних библиотек функции, упаковывает их в std::bind и заносит в std::map<std::string, std::function>. В дальнейшем, как это очевидно, по некой произвольной строке мы в map находим нужную нам функцию и ее выполняем. Система написана на С++11 под Linux (Ubuntu 18.04)

В самом коде, который реализует этот механизм утечек памяти нет (я надеюсь, проверял разными способами). А вот дальше начинается магия. Если в импортируемой функции буду использовать std::string, то память после выполнения импортированной функции не будет освобождаться.

Бодаюсь с этим уже третий день ))

Три вопроса:
1. Что за фигня происходит? Может кто-то сталкивался с подобным поведением.
2. Какие инструменты можно использовать для поиска утечек памяти? Может я еще не все испробовал )
3. Кто готов покопаться в коде системы и поискать ошибку (за вознаграждение, естественно)?
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
06.12.2019, 13:28
Ответы с готовыми решениями:

Std::string портит память
Столкнулся с интересной проблемой. Лет 6 назад использовал одну прогу под ASPLinux 7.2. Исходники компилировались нормально и прога...

Освобождение памяти после std:bind
Что имею: - CentOS - gcc 4.8 - valgrind Что делаю - С помощью valgrind устраняю утечки - Если приложение остановлено...

Не могу разобраться как обновить в std::map<std::string, вектор_структур>
Не могу разобраться как обновить вектор структур после его добавления в map без удаления и перезаписи struct pStruct { int...

27
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
06.12.2019, 15:49
Цитата Сообщение от igor_goryachev Посмотреть сообщение
В самом коде, который реализует этот механизм утечек памяти нет (я надеюсь, проверял разными способами). А вот дальше начинается магия. Если в импортируемой функции буду использовать std::string, то память после выполнения импортированной функции не будет освобождаться.
Что значит "в импортируемой функции буду использовать std::string"?
Покажи эту функцию
0
0 / 0 / 0
Регистрация: 02.02.2016
Сообщений: 14
07.12.2019, 22:07  [ТС]
Печаль всей ситуации в том, что не важно, что внутри... Изначально был код, который открывал один файл, что-то с ним делал и записывал в другой файл. Вот такой код в системе выполняется бесконечно (сам на себя зациклен):

C
1
2
3
4
5
6
7
8
9
10
std::string A2Files::test(const std::shared_ptr<message_t2> &message, const Json::Value &route_params)
{ LOG_TRACE
  /////////////////////////////////////////////////////////////////////////////////////
 
      std::cout << "[END TEST]" << std::endl;
      this->sendMessage(this->name,"/test",message->root);
 
  /////////////////////////////////////////////////////////////////////////////////////
  return "success2";
};
При этом утечки памяти нет. Все стабильно и радостно )

Сейчас я свел все к такой примитивной штуке:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
std::string A2Files::test(const std::shared_ptr<message_t2> &message, const Json::Value &route_params)
{ LOG_TRACE
  /////////////////////////////////////////////////////////////////////////////////////
 
      std::string *data = new std::string(" ");
      
      for(int i = 0; i < 500000; i++)
      {
         data->append("<sdfdsfsdfdsff><bbnknhjkjlkjk>sdfdsfds</sdfdsfdsf></sdfdsfd>\r\n");
      } 
 
      data->clear();
      delete data;
 
      this->sendMessage(this->name,"/test",message->root);
 
  /////////////////////////////////////////////////////////////////////////////////////
  return "success2";
};
И каждый вызов функции приводит к увеличению памяти *(
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
08.12.2019, 11:10
Цитата Сообщение от igor_goryachev Посмотреть сообщение
При этом утечки памяти нет. Все стабильно и радостно )
Цитата Сообщение от igor_goryachev Посмотреть сообщение
И каждый вызов функции приводит к увеличению памяти *(
Здесь утечки нет. Она где-то в другом месте, либо в sendMessage, либо в твоём json-парсере.
Я так понимаю, ты используешь gcc. Не знаю, как смотреть там утечки (обычно достаточно посмотреть код), но может вот это поможет https://github.com/google/sani... kSanitizer

Ну и, если уж создаёшь динамические объекты, пользуйся std::unique_ptr
C++
1
2
3
4
5
6
7
8
 /////////////////////////////////////////////////////////////////////////////////////
 
       std::unique_ptr<std::string> data{new std::string(" ")};
      
      for(int i = 0; i < 500000; i++)
....................
      //data->clear();
      //delete data;
0
0 / 0 / 0
Регистрация: 02.02.2016
Сообщений: 14
08.12.2019, 12:04  [ТС]
Спасибо за рекомендации!

Как вы правильно написали, в приведенном коде ошибок нет. Но достаточно убрать из функции код работы со строкой, как утечка памяти прекращается. Соответственно, утечек памяти ни в sendMessage, ни в Json парсере нет. Может ли это быть связано с тем, что функция вызывается не напрямую, а через std::bind?
0
19491 / 10097 / 2460
Регистрация: 30.01.2014
Сообщений: 17,805
08.12.2019, 13:14
igor_goryachev, в вашей ситуации их общих рекомендаций можно только отметить, что лучше не передавать объекты стандартной библиотеки через границы модулей. Это чревато нарушением ODR и последующим за этим неопределенным поведением программы.
0
0 / 0 / 0
Регистрация: 02.02.2016
Сообщений: 14
10.12.2019, 13:56  [ТС]
Я передаю за пределы модулей объекты. Может в этом проблема )

1. Есть основная программа. В ней реализован некоторый базовый класс и "реестр" подключаемых библиотек.

2. Внутри каждой библиотеки реализован один класс (наследуемый от базового). Сам класс передается в основную программу примерно так:

C++
1
2
3
4
extern "C" void addFactory(ActorList *factory)
{
   factory->add<A2Files>("A2Files");
}
Класс ActorList реализован тоже довольно просто:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class ActorList
{
  private:
    std::map <std::string, Actor2*> actors;  // коллекция акторов по именам
    std::map <std::string, std::function<Actor2*(std::string, Json::Value)> > FM;
    std::map<std::string, std::function<std::string(const std::shared_ptr<message_t2>, const Json::Value &)> > *ActorRouter;
 
    std::mutex create_actor_mutex;
 
  protected:    
 
   // Функция создания актора
       template<class C> C* createA(const std::string & name, Json::Value data) const { return new C(name,data); };
 
  public: 
 
 
    ActorList();
    ~ActorList();
 
   void init();
    
 
   template <class C>
   void add(const std::string & type)
   {
       auto it = this->FM.find(type);
       if (it == this->FM.end())
       {
          this->FM.insert( std::pair<std::string, std::function<Actor2*(std::string, Json::Value)>>(type, std::bind(&ActorList::createA<C>, this, std::placeholders::_1, std::placeholders::_2) ) );
       }
    };
 
}
Я не вижу где тут может возникнуть нарушение ODR...
0
фрилансер
 Аватар для Алексей1153
6442 / 5636 / 1127
Регистрация: 11.10.2019
Сообщений: 14,983
10.12.2019, 14:04
igor_goryachev, с объектом класса должен работать тот модуль, который его создал. Тот же самый модуль должен и удалять объект. Наружу можно передавать указатель на созданный объект. На любой чих нужно передавать указатель в создавший объект модуль, чтобы тот модуль вызвал нужную функцию объекта

Это всё потому, что std::string в одном модуле может быть устроен совсем иначе, чем std::string в другом, если это разные процессы
0
19491 / 10097 / 2460
Регистрация: 30.01.2014
Сообщений: 17,805
10.12.2019, 14:15
Цитата Сообщение от Алексей1153 Посмотреть сообщение
std::string в одном модуле может быть устроен совсем иначе, чем std::string в другом,
igor_goryachev, Даже если они устроены одинаково. ODR мы все равно нарушим, ведь почти вся внутрянка std::string - это функции в заголовочном файле. В одном модуле (библиотеке) будут в итоге одни определения, а в другом (программе, которая ее использует) - другие. При этом к объекту будут сначала применяться одни версии этих функций (пока он на стороне библиотеки), а затем другие (когда его получили на стороне программы). Это и есть нарушение ODR.
Нарушение ODR как правило не диагностируется, и далеко не всегда приводит к видимым проблемам. Этим оно и опасно.
0
0 / 0 / 0
Регистрация: 02.02.2016
Сообщений: 14
10.12.2019, 14:15  [ТС]
Процесс один. Потоки разные...

Спасибо за указание куда думать. Фактически, сейчас у меня библиотека отдает основной программе "право" создавать и управлять классом, который реализован в этой библиотеке. Нужно же переписать, что управление жизненным циклом должно быть реализовано в библиотеках, а основная программа должна просто вызывать соответствующие импортированные из библиотек функции?

Грубо говоря, вместо

C++
1
extern "C" void addFactory(ActorList *factory)
У меня должно быть что-то типа такой функции:

C++
1
2
3
4
extern "C" A2Files * new_actor()
{
    return new A2Files();
}
0
19491 / 10097 / 2460
Регистрация: 30.01.2014
Сообщений: 17,805
10.12.2019, 14:22
igor_goryachev, есть отличная книга, Martin Reddy - API Design for C++.
Там есть ответы на почти все вопросы о том, как правильно дизайнить библиотеки и плагины с использованием С и С++.
2
0 / 0 / 0
Регистрация: 02.02.2016
Сообщений: 14
10.12.2019, 14:25  [ТС]
Спасибо, почитаю
0
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
11.12.2019, 10:52
Цитата Сообщение от igor_goryachev Посмотреть сообщение
Если в импортируемой функции
тебя в этой фразе ничего не настораживает?

Цитата Сообщение от igor_goryachev Посмотреть сообщение
Я передаю за пределы модулей объекты. Может в этом проблема
есть такой железобетонный принцип:
кто память выделял, тот и должен её освобождать.

схема выглядит примерно так:

C++
1
2
3
4
5
// ресурс создаётся менеджером импортированной функции
auto* resource = api::create(); 
 
// возвращаем ресурс менеджеру на стороне модуля
api::free(resource);
на принимающей стороне у тебя один менеджер памяти,
на стороне модуля - другой.

если выделять память одним, а освобождать другим,
то утечки памяти - это ты ещё легко отделался.
0
0 / 0 / 0
Регистрация: 02.02.2016
Сообщений: 14
11.12.2019, 16:57  [ТС]
hoggy, Все манипуляции со строкой происходят в пределах одной функции. За пределы модуля передается объект. Функция объекта оборачивается в std::bind и много раз вызывается. Т.е. строка там где создается там и удаляется. передачи строки за границы модуля (даже функции) не осуществляется.

Добавлено через 2 минуты
Я сейчас читаю рекомендованную книжку и с нуля пишу весь код, собирая его по кусочкам ) Теперь постараюсь ошибок подобных не совершить
0
0 / 0 / 0
Регистрация: 02.02.2016
Сообщений: 14
12.12.2019, 17:27  [ТС]
DrOffset, oleg-m1973, hoggy

Всем привет!

Переписал я свой код с учетом всех рекомендаций. Запустил. Модули динамически загружаются, функции вызываются, память освобождается. Полная красота и гармония! В однопоточном режиме.

Запустил это все дело на пуле потоков и получил утечку памяти ( Видимо, дело в пуле потоков
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
12.12.2019, 17:36
Цитата Сообщение от igor_goryachev Посмотреть сообщение
Запустил это все дело на пуле потоков и получил утечку памяти ( Видимо, дело в пуле потоков
Это вряд ли. Скорее, проблема в твоём коде.
0
0 / 0 / 0
Регистрация: 02.02.2016
Сообщений: 14
12.12.2019, 17:41  [ТС]
oleg-m1973, Почему? В однопоточном режиме все отлично же
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
12.12.2019, 17:43
Цитата Сообщение от igor_goryachev Посмотреть сообщение
oleg-m1973, Почему? В однопоточном режиме все отлично же
Не знаю. Я ни того ни другого не видел.
0
0 / 0 / 0
Регистрация: 02.02.2016
Сообщений: 14
12.12.2019, 20:37  [ТС]
Уважаемые знатоки!

Вот какой вопрос )

Есть примитивный до безумия класс:


C++
1
2
3
4
5
6
7
8
9
10
11
class ThreadPool
{
  public:
 
    ThreadPool();
    ~ThreadPool();
 
    void thread_fn(int i);
    void test();
 
};
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include "thread.h"
 
 
ThreadPool::ThreadPool(){  };
ThreadPool::~ThreadPool(){ };
 
    void ThreadPool::thread_fn(int i)
    {
       while (true)
       {
          this->test();
          std::cout << "thread" << std::endl;
          std::this_thread::sleep_for(std::chrono::seconds(3));
       }
    };
 
    void ThreadPool::test()
    {
       std::string tmp(" ");
 
       for(int i = 0; i < 500000; i++)
       {
          tmp.append("<test>data</test>");
       }
       std::this_thread::sleep_for(std::chrono::seconds(3));
    };
Пишу вот так в однопоточном режиме:

C++
1
2
3
4
5
int main(int argc, char* argv[])
{   
   auto threads = new ThreadPool();
   threads->thread_fn(10);
}
Запускаю команду top и наблюдаю как прыгают цифры потребления памяти. Сначала память растет (в момент выполнения функции ThreadPool::test(), а потом освобождается. Все великолепно.

Немного меняю код
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
int main(int argc, char* argv[])
{   
 
   auto threads = new ThreadPool();
 
   std::thread t1(&ThreadPool::thread_fn, threads, 10);
 
   while(true)
   {
      std::this_thread::sleep_for(std::chrono::seconds(3));
   }
 
}
Запускаю top... и не вижу характерных скачков памяти. Память выделилась и не освобождается. (Хорошо хоть не растет дальше).

Внимание вопрос. Почему???
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
12.12.2019, 21:09
Цитата Сообщение от igor_goryachev Посмотреть сообщение
Запускаю top... и не вижу характерных скачков памяти. Память выделилась и не освобождается. (Хорошо хоть не растет дальше).
Внимание вопрос. Почему???
Наверное, потому что top криво работает с потоками (linux всё-таки). Код абсолютно одинаковый (кривой, конечно, но это другое)
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
12.12.2019, 21:09
Помогаю со студенческими работами здесь

std::string, std::fstream, ошибка кучи
где то начало вылетать при операции += с локальной переменной std::string. Заменил на свой qString. Замечательно, то же самое... ошибка при...

ошибка error: cannot convert 'std::string {aka std::basic_string<char>}' to 'std::string* {aka std::basic_stri
на вод поступают 2 строки типа string. определить количество вхождений строки 2 в строку 1 ошибка error: cannot convert 'std::string {aka...

Std::bind, std::mem_fun, std::mem_fn
В чем разница между функциями std::bind, std::mem_fun, std::mem_fn?

Как можно еще использовать std::placeholders вне в связки с std::bind?
Добрый день! Как можно еще использовать std::placeholders вне в связки с std::bind?

В чем отличия между std::cref() и std::bind()?
В документации не понял, что делает bind() ? И чем отличается cref() от операции взятия адреса? int x; int *y = &amp;x;...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
PhpStorm 2025.3: WSL Terminal всегда стартует в ~
and_y87 14.12.2025
PhpStorm 2025. 3: WSL Terminal всегда стартует в ~ (home), игнорируя директорию проекта Симптом: После обновления до PhpStorm 2025. 3 встроенный терминал WSL открывается в домашней директории. . .
Access
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
Создание Single Page Application на фреймах
krapotkin 16.11.2025
Статья исключительно для начинающих. Подходы оригинальностью не блещут. В век Веб все очень привыкли к дизайну Single-Page-Application . Быстренько разберем подход "на фреймах". Мы делаем одну. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru