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

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

06.12.2019, 13:28. Показов 2910. Ответов 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
19500 / 10105 / 2461
Регистрация: 30.01.2014
Сообщений: 17,818
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
6465 / 5679 / 1131
Регистрация: 11.10.2019
Сообщений: 15,122
10.12.2019, 14:04
igor_goryachev, с объектом класса должен работать тот модуль, который его создал. Тот же самый модуль должен и удалять объект. Наружу можно передавать указатель на созданный объект. На любой чих нужно передавать указатель в создавший объект модуль, чтобы тот модуль вызвал нужную функцию объекта

Это всё потому, что std::string в одном модуле может быть устроен совсем иначе, чем std::string в другом, если это разные процессы
0
19500 / 10105 / 2461
Регистрация: 30.01.2014
Сообщений: 17,818
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
19500 / 10105 / 2461
Регистрация: 30.01.2014
Сообщений: 17,818
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
Ответ Создать тему
Новые блоги и статьи
Автозаполнение реквизита при выборе элемента справочника
Maks 27.03.2026
Программный код из решения ниже на примере нетипового документа "ЗаявкаНаРемонтСпецтехники" разработанного в конфигурации КА2. При выборе "Спецтехники" (Тип Справочник. Спецтехника), заполняется. . .
Сумматор с применением элементов трёх состояний.
Hrethgir 27.03.2026
Тут. https:/ / fips. ru/ EGD/ ab3c85c8-836d-4866-871b-c2f0c5d77fbc Первый документ красиво выглядит, но без схемы. Это конечно не даёт никаких плюсов автору, но тем не менее. . . всё может быть. . .
Автозаполнение реквизитов при создании документа
Maks 27.03.2026
Программный код из решения ниже размещается в модуле объекта документа, в процедуре "ПриСозданииНаСервере". Алгоритм проверки заполнения реализован для исключения перезаписи значения реквизита,. . .
Команды формы и диалоговое окно
Maks 26.03.2026
1. Команда формы "ЗаполнитьЗапчасти". Программный код из решения ниже на примере нетипового документа "ЗаявкаНаРемонтСпецтехники" разработанного в конфигурации КА2. В качестве источника данных. . .
Кому нужен AOT?
DevAlt 26.03.2026
Решил сделать простой ланчер Написал заготовку: dotnet new console --aot -o UrlHandler var items = args. Split(":"); var tag = items; var id = items; var executable = args;. . .
Отправка уведомления на почту при создании или изменении элементов справочника
Maks 25.03.2026
Программная отправка письма электронной почты на примере типового справочника "Склады" в конфигурации БП3. Перед реализацией необходимо выполнить настройку системной учетной записи электронной. . .
модель ЗдравоСохранения 5. Меньше увольнений- больше дохода!
anaschu 24.03.2026
Теперь система здравосохранения уменьшает количество увольнений. 9TO2GP2bpX4 a42b81fb172ffc12ca589c7898261ccb/ https:/ / rutube. ru/ video/ a42b81fb172ffc12ca589c7898261ccb/ Слева синяя линия -. . .
Midnight Chicago Blues
kumehtar 24.03.2026
Такой Midnight Chicago Blues, знаешь?. . Когда вечерние улицы становятся ночными, а ты не можешь уснуть. Ты идёшь в любимый старый бар, и бармен наливает тебе виски. Ты смотришь на пролетающие. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru