LINUX.ORG.RU

Объявление и очистка переменных в цикле C

 ,


0

4

Приветствую.

Обновил код.

Суть такова: получаем список активных окон в иксах, запускаем на них цикл for, лезем в dbus к сервису bamf для получения информации об этих окнах.

На самом деле функция не main(), а другая, но не суть - она вызывается в бесконечном цикле столько сколько работает главная программа.

При использовании gobject'ных функций дерганья dbus, начинается небольшая утечка памяти, не связанная с количеством итераций. Плюс-минус 100-200 байт в минуту.

Может я как-то не правильно объявляю или не там подчищаю переменные?

/*
Это не важно.

#include <phpcpp.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <glib/gprintf.h>
#include <gio/gio.h>
#include <map>
#include <string>
#include <bits/stdc++.h>
#include <gtk/gtk.h>
#include <gtk/gtkx.h>
#define WNCK_I_KNOW_THIS_IS_UNSTABLE 1
#include <libwnck/libwnck.h>
#include <gdk/gdkx.h>
#include <err.h>
#include <X11/XKBlib.h>
*/
void main() {
  WnckScreen *screen;
  WnckWindow *active_window;
  GList *window_l;
  gboolean act;
  GError *error = NULL;
  screen = wnck_screen_get_default();
  wnck_screen_force_update(screen);
  active_window = wnck_screen_get_active_window(screen);
  GDBusConnection *connection = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
  int n = 0;
  for (window_l = wnck_screen_get_windows(screen); window_l != NULL;
       window_l = window_l->next) {
    WnckWindow *window = WNCK_WINDOW(window_l->data);
    // Только если окно - настоящее, а не панель\меню\рабочий стол
    if (wnck_window_get_window_type(window) == WNCK_WINDOW_NORMAL) {
      n++;
      WnckApplication *app = wnck_window_get_application(window);
      unsigned long pid = wnck_window_get_pid(window);
      unsigned long xid = wnck_window_get_xid(window);
      std::string sxid = std::to_string(xid);
      std::string object = "/org/ayatana/bamf/window/" + sxid;
      char *newobject = new char[object.length() + 1];
      strcpy(newobject, object.c_str());
/* 

Вот этот блок, вызывает утечку памяти, не понятно с чем связанную, в рандомные промежутки времени


      GDBusMessage *call_message = g_dbus_message_new_method_call(
          "org.ayatana.bamf", newobject, "org.ayatana.bamf.view", "Parents");
      GDBusMessage *reply_message =
          g_dbus_connection_send_message_with_reply_sync(
              connection, call_message, G_DBUS_SEND_MESSAGE_FLAGS_NONE, -1,
              NULL, NULL, &error);
      GVariant *result = g_dbus_message_get_body(reply_message);
      g_variant_unref(result);
      g_variant_unref(reply_child);
      g_object_unref(call_message);
      g_object_unref(reply_message);
      g_free(error);
*/
    }
  }
g_object_unref(connection);
}

В оригинальном полном коде - утечка памяти прекращается при закомментировании вот этих функций, т.е. проблема не в других участках кода однозначно.

Прошу прощения за то что криво объясняю - я не программист, и уж тем более не программист на С.

Благодарю.

★★★★★

Последнее исправление: windows10 (всего исправлений: 4)

Правильно очищать так, что бы не было утечек памяти. В цикле, или за пределом, зависит от кода.


void main1() {
 sometype *somevar;
 somevalue = malloc();
 while (something) {
  somevar = somevalue;
  // Делаем что-то с переменной
 }
 free(somevar);
}

void main2() {
 sometype *somevar;
 while (something) {
  somevalue = malloc();
  somevar = somevalue;
  // Делаем что-то с переменной
  free(somevar);
 }
}

В документации к glib обычно указывают кто владелец памяти и должен ли ты ее очищать. Можешь попробовать вместо нее glibmm, он делает простой код проще, а сложный невозможным для понимания если не можешь написать простой.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 3)

если somevalue это не буквальное value а нечто что создаётся в этом цикле и отводится под это дин али на стеке память то внутри (да даже перед переустановкой somevar) отпускать старую память обратно в которой текущее somevalue

более того если somevalue это составное нечно с указателями ещ куда то вроде как free не освобождает все внешние вложения поэтому явно следует чем то более адекватным Gobject возвращать

ну и куп де этатгрэйс:

может память течёт в твоём непосредственно коде а потому что ты у Gobject чёт запрашиваешь а потом не отдаёш по их протоколу и следовательно происходит накопление устаревшего в памяти твоего процеса

домыслы без помыслов

qulinxao3 ★★
()
Последнее исправление: qulinxao3 (всего исправлений: 1)
void main() {
 sometype *somevar;
 while (something) {
  somevar = somevalue;
  // Делаем что-то с переменной
 }
 free(somevar);
}

этот код вообще мусорный. обявляется указатель, потом ему в цикле присваиваются другие указатели(указатель)… потом делитится последний присвоенный. откуда берутся такие алгоритмы - непонятно.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)

у тя семантика в первом присвоении отличается от второго присвоения

в первом случае ты выставляеш указатель в значение somevalue

во втором ты помещаеш на указываемое somevalue

если не опечатка ты в первом случае вообще непонятно что освобождаеш

в прочем во втором тоже :)

qulinxao3 ★★
()

Объявление и очистка

Абсолютно независимые действия с точки зрения использования памяти. Сформулий вопрос правильно.

anonymous
()

для первого блока такой вот код является корректным…

void main() {
 sometype *somevar = somevalue;
 while (something) {
  // Делаем что-то с *somevar
 }
 free(somevar);
}

если конечно не задумываться о природе somevalue. это должен быть указатель на обьект на куче.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)

Это вам не пэхапе!

Один вызов free() должен соответствовать одному вызову функции malloc() (или чего у тебя там должно быть в somevalue). sometype *somevar это всего лишь указатель, жить он будет на стеке ровно до закрытия блока {} на своём уровне.

Сломал башку. Gobject’ные функции для работы с dbus - текут. И не могу понять, где именно.

Ну так юзай valgrind.

Bfgeshka ★★★★★
()

free - освобождение обьекта на куче, взятого по malloc. это обьекты, которые нужны временно. на указатели на обьекты, расположенные не на куче, например на стеке или в глобальной памяти, free вызывать нельзя. менеджер кучи кучи ругнется, что это не его адреса, и программа вылетит.

alysnix ★★★
()

GObject работает на подсчёте ссылок. В твоих примерах нет ни ref, ни unref. Зато есть free, который вообще из параллельной вселенной. Зачем приводить пример, который не иллюстрирует вообще ничего?

ox55ff ★★★★★
()
Ответ на: комментарий от MOPKOBKA

В документации к glib обычно указывают кто владелец памяти и должен ли ты ее очищать

Да. Я просто утрировал код. Там g_object_unref используется согласно их собственным примерам. Но все равно течет.

windows10 ★★★★★
() автор топика
Ответ на: комментарий от qulinxao3

может память течёт в твоём непосредственно коде а потому что ты у Gobject чёт запрашиваешь а потом не отдаёш по их протоколу

Как вариант, просплюсь - проверю. Благодарю.

windows10 ★★★★★
() автор топика
Ответ на: комментарий от qulinxao3

если не опечатка ты в первом случае вообще непонятно что освобождаеш

Думаю лучше днем причешу говнокод и спрошу как есть, без абстрактных примеров =)

windows10 ★★★★★
() автор топика
Ответ на: комментарий от ox55ff

GObject работает на подсчёте ссылок. В твоих примерах нет ни ref, ни unref. Зато есть free, который вообще из параллельной вселенной. Зачем приводить пример, который не иллюстрирует вообще ничего?

Ты прав. Просто оригинальный код весьма неудобен чтобы его тут выкладывать. Чуть позже выложу, а пока спасибо =)

windows10 ★★★★★
() автор топика
Ответ на: комментарий от Bfgeshka

Ну так юзай valgrind.

А ты юзал valgrind с программой которая юзает glib? У последнего там вообще «своя атмосфера» по работе с памятью, из за чего профайлинг подобного превращается в ад.

ИМХО, лучше юзать профайлинг памяти который предоставляет сам glib: G_DEBUG=gc-friendly, G_SLICE=always-malloc, G_DEBUG=objects и g_mem_set_vtable(). Ну а valgrind-ом треккать только память, которая выдедается при помощи malloc().

iron ★★★★★
()
Ответ на: комментарий от windows10
 g_variant_unref(reply_child);

…_unref - это насколько я понимаю, ручное ведение счетчика ссылок, и это нужно делать показывая, что это обьект больше не используешь.

но что такое reply_child? откуда он взялся, в коде его вроде нигде не описывали. и почему тут ему уменьшают счетчик?

судя по тому, что этого reply_child нет внутри цикла, ему и unref нужно делать не тут.

alysnix ★★★
()
Ответ на: комментарий от windows10

Ты уверен, что ты правильно освобождаешь ресурсы? Для ошибки, случаем, это не так g_error_free (error); делается?

У тебя, кажись, еще здесь протекает:

char *newobject = new char[object.length() + 1];
PRN
()
Последнее исправление: PRN (всего исправлений: 2)
Ответ на: комментарий от alysnix

Я просто не все удалил с оригинального кода, чтобы вменяемо умостить все в пример.

Меня интересует другое.

for (int count = 0; count < 10; ++count) {
 GDBusMessage *call_message = g_dbus_message_new_method_call(
          "org.ayatana.bamf", "123456", "org.ayatana.bamf.view", "Parents");
}

Чистить структуру (неважно как, g_free, g_clear_object, или как-то по-другому) нужно после каждого использования внутри цикла for, или достаточно один раз ПОСЛЕ завершения цикла?

windows10 ★★★★★
() автор топика
Ответ на: комментарий от windows10

там функции возвращают аллоцированный данным вызовом обьект, и после его использования его надо удалить. иначе будет утечка. то есть каждому взятию обьекта (то есть вызову функции возвращающей обьект), должен быть сопоставлен свой free, или unref), короче

- получил от функции обьект
- попользовал
- неким описанным образом правильно "удалил"
alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от windows10

Я ваших глибов не знаю, но вот это подозрительно

for (int count = 0; count < 10; ++count) {
 GDBusMessage *call_message = g_dbus_message_new_method_call(
          "org.ayatana.bamf", "123456", "org.ayatana.bamf.view", "Parents");
}

тут функция дает обьект, но он никак не деаллоцируется клиентским кодом. это надо док на g_dbus_message_new_method_call читать, что такое она возвращает, и надо ли это освободить.

ну и вот https://docs.gtk.org/gio/ctor.DBusMessage.new_method_call.html

Type: GDBusMessage

A GDBusMessage. Free with g_object_unref().
The caller of the function takes ownership of the data, and is responsible for freeing it.

то есть, тут, по докам, утечка…

а что это вообще писал???

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 2)
Ответ на: комментарий от PRN

Ты уверен, что ты правильно освобождаешь ресурсы? Для ошибки, случаем, это не так g_error_free (error); делается?

Не уверен, усиленно гуглю. Сдирал с примеров на гите, там после каждого запроса просто юзается g_variant_unref / g_object_unref, но у них это делается всего один раз. У меня же это делается в цикле.

Поэтому либо gobject дырявый, но это слабо заметно потому что эти вызовы не крутят в цикле, а лишние 100 байт незаметны среди гигов, либо же я дурак и не правильно освобождаю ресурсы. Я склонен ко второму =)

У тебя, кажись, еще здесь протекает:

Нет, здесь точно не протекает, равно как и в других местах, потому что стоит вот эти функции закомментировать, и память не течет.

windows10 ★★★★★
() автор топика
Ответ на: комментарий от alysnix

тут функция дает обьект, но он никак не деаллоцируется клиентским кодом. это надо док на g_dbus_message_new_method_call читать, что такое она возвращает, и надо ли это освободить.

Та вот освобождаю разными методами, но все равно течет.

а что это вообще писал???

Панель задач.

То что касается иксов - работает как часики. Собственно оно как часики и работало. Стоило влезть в dbus - началась течь.

windows10 ★★★★★
() автор топика
Ответ на: комментарий от windows10

Та вот освобождаю разными методами, но все равно течет

освобождать надо методом описанным в доке

 Free with g_object_unref().

а не разными.

в цикле, что ты привел, никакого g_object_unref() нет, и тут создается 10 обьектов, все они присваиваются в один указатель, и потому остатается только адрес последнего. а 9 теряются.

или ты опять привел только «кусок»?

цикл должен выглядеть как-то так

for (int count = 0; count < 10; ++count) {
 GDBusMessage *call_message = g_dbus_message_new_method_call(
          "org.ayatana.bamf", "123456", "org.ayatana.bamf.view", "Parents");
...
//тут что то делаем с этим call_message
...
  g_object_unref(call_message); ///тут отпускаем call_message

}
alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от alysnix

Невероятно, но факт. Все равно течет.

Впрочем течет не оно, течет их GVariant, ни С ни я тут ни при чем. g_variant_unref не помогает, видимо из-за того что оно принимает в себя массив строк, который потом не очищает.

Буду думать как это пофистить. Благодарю.

windows10 ★★★★★
() автор топика

Всех благодарю, решето найдено, пришлось в valgrind.

Всему виной GVariant, содержащий массив строк (as). Перед очисткой, нужное значение нужно копировать в другую строку при помощи g_strdup.

Всем бобра !

windows10 ★★★★★
() автор топика