Russian Qt Forum
Ноябрь 22, 2024, 17:32 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2 3 ... 5   Вниз
  Печать  
Автор Тема: QObject и memory leaks  (Прочитано 36574 раз)
sq_vasya
Гость
« : Август 25, 2010, 16:38 »

Всем привет.

Столкнулся с проблемой утечки памяти в своём приложении. После долгих мучений пришёл к вырожденому случаю, который даёт утечки.

Код:
    int objectsAmount = 300;
    QVector<QObject*> objectsVector;
    objectsVector.resize( objectsAmount );

    __LEAKS_CHECK_START;

    for ( int i = 0; i < objectsAmount; i++ )
        {
        QObject* object = new QObject;
        objectsVector.append( object );
        }

    for ( int i = 0; i < objectsAmount; i++ )
        {
        delete objectsVector.at( i );
        }

    int res = 0;

    __LEAKS_CHECK_END;

данный код вставленный в только что сгенерированное визардом приложение даёт 600 убежавших ячеек.
Для проверки утечек использую

Код:
#define __LEAKS_CHECK_START \
    TInt cellsCountStart = User::CountAllocCells(); \
    qDebug( "cellsCountStart = %d", cellsCountStart ); \

#define __LEAKS_CHECK_END \
    TInt cellsCountEnd = User::CountAllocCells(); \
    qDebug( "cellsCountEnd = %d", cellsCountEnd ); \
    if ( (cellsCountEnd - cellsCountStart) == KLeaksAllowable ) \
        qDebug( "No memory leaks detected" ); \
    else \
        qDebug( "%d memory leaks detected", cellsCountEnd - cellsCountStart - KLeaksAllowable );
работает только для Symbian.

Кто нибудь может мне объяснить, куда девается память и что нужно делать, чтобы она не текла?
Записан
sq_vasya
Гость
« Ответ #1 : Август 25, 2010, 16:39 »

если вместо QVector::resize() использовать QVector::reserve(), то получаю 258 убежавших ячеек.
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #2 : Август 25, 2010, 17:39 »

в вектор, куда же еще...
Записан
sq_vasya
Гость
« Ответ #3 : Август 25, 2010, 18:12 »

в вектор, куда же еще...

Не согласен... Для чистоты эксперимента вынес вектор и работу с ним в отдельную ф-ию. Плюс разместил вектор на хипе и по честному удалил ручками. Результат не изменился.

Код:
void JustDoIt()
    {
    int objectsAmount = 300;
    QVector<QObject*>* objectsVector = new QVector<QObject*>;
    objectsVector->reserve( objectsAmount );

    for ( int i = 0; i < objectsAmount; i++ )
        {
        QObject* object = new QObject;
        objectsVector->append( object );
        }

    for ( int i = 0; i < objectsAmount; i++ )
        {
        delete objectsVector->at( i );
        }

    objectsVector->clear();
    delete objectsVector;
    }

int main(int argc, char *argv[])
    {

    __LEAKS_CHECK_START;

    JustDoIt();

    __LEAKS_CHECK_END;

    return 0;
    }

ещё какие будут идеи, господа?
Записан
Alex Custov
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2063


Просмотр профиля
« Ответ #4 : Август 25, 2010, 18:27 »

1) Как ты думаешь, сколько элементов в векторе будет после выполнения этого кода?

Код:
    int objectsAmount = 300;
    QVector<QObject*> objectsVector;
    objectsVector.resize( objectsAmount );

    for ( int i = 0; i < objectsAmount; i++ )
    {
        QObject* object = new QObject;
        objectsVector.append( object );
    }

Варианты:

  • 300
  • 600

2) Как ты думаешь, что будет удалять оператор delete, исходя из предыдущего ответа?

Записан
JamS007
Гость
« Ответ #5 : Август 25, 2010, 18:51 »

Alex Custov, опередили Вы меня...

QVector::reserve() - резервирует место в векторе, то есть создает в вашем случае 300 элементов.
А потом вы добавляете еще 300 элементов. После чего удаляете их... А остальные элементы, то есть, те что остались после reserve() не удаляете.
Записан
sq_vasya
Гость
« Ответ #6 : Август 25, 2010, 18:53 »

1) Как ты думаешь, сколько элементов в векторе будет после выполнения этого кода?

Код:
    objectsVector.resize( objectsAmount );

Варианты:

  • 300
  • 600
именно этого - 600.

Цитировать
2) Как ты думаешь, что будет удалять оператор delete, исходя из предыдущего ответа?
я бы на его месте сломался с кодом KERNEL-EXEC 3. Хотя нет, если там забито NULL-ами, то оператор delete корректно это отработает.

Дальше.

Цитировать
если вместо QVector::resize() использовать QVector::reserve(), то получаю 258 убежавших ячеек.
Это раз.

Цитировать
Код:
void JustDoIt()
    {
    int objectsAmount = 300;
    QVector<QObject*>* objectsVector = new QVector<QObject*>;
    objectsVector->reserve( objectsAmount );
...
Это два. Именно reserve а не resize.

Да, прошу прощения, немного ввёл в заблуждение, но если читать внимательно, то всё корректно (на мой взгляд, поправте, если я ошибаюсь).  "Ответ #3" как по мне вполне себе рабочий и указанную ошибку не содержит.
Записан
JamS007
Гость
« Ответ #7 : Август 25, 2010, 18:57 »

Вы храните вектор указателей на объект, а не вектор объектов. clear() удаляет все из вектора, то есть указатели, не удаляя объекты, на которые они указывают. И даже то, что Вы удаляете сам вектор не приводит к удалению элементов из памяти, повторюсь, вы сохраняете указатели на них, а не сами элементы. При удалении вектора исчезнут указатели, а элементы останутся.
Записан
sq_vasya
Гость
« Ответ #8 : Август 25, 2010, 18:57 »

Если мне не верите, то можете попробовать прологировать размер вектора.

Код:
    for ( int i = 0; i < objectsAmount; i++ )
        {
        QObject* object = new QObject;
        objectsVector->append( object );
        qDebug( "objectsVector->size() = %d", objectsVector->size() );
        }

будет от 1 до 300. Проверял.
Записан
sq_vasya
Гость
« Ответ #9 : Август 25, 2010, 19:00 »

Вы храните вектор указателей на объект, а не вектор объектов. clear() удаляет все из вектора, то есть указатели, не удаляя объекты, на которые они указывают. И даже то, что Вы удаляете сам вектор не приводит к удалению элементов из памяти, повторюсь, вы сохраняете указатели на них, а не сами элементы. При удалении вектора исчезнут указатели, а элементы останутся.

Я осознаю, что храню в векторе уазатели на объекты. Эти объекты я удаляю кодом
Код:
for ( int i = 0; i < objectsAmount; i++ )
        {
        delete objectsVector->at( i );
        }

а
Код:
objectsVector->clear();
вызываю для очистки совести. На результат это всё равно не влияет.
Записан
Rcus
Гость
« Ответ #10 : Август 25, 2010, 19:16 »

Хм... QVector использует qMalloc -> ::malloc -> User::Alloc() -> RHeap::Alloc (очень просто аллокатор памяти, но там есть трассировка выделенных кусков)
не понимаю что тут может быть не так. А какие результаты даёт выполнение функции несколько раз? По идее если количество памяти стабилизируется то это не страшно. Под GNU/Linux-x86_64 Valgrind тоже пугает LEAK SUMMARY: still reachable: 18,608 bytes in 270 blocks, но детали все ставят на место.
Записан
Пантер
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 5876


Жаждущий знаний


Просмотр профиля WWW
« Ответ #11 : Август 25, 2010, 19:20 »

const T & QVector::at ( int i ) const
?
Записан

1. Qt - Qt Development Frameworks; QT - QuickTime
2. Не используйте в исходниках символы кириллицы!!!
3. Пользуйтесь тегом code при оформлении сообщений.
Sancho_s_rancho
Гость
« Ответ #12 : Август 25, 2010, 20:18 »

const T & QVector::at ( int i ) const
?
Меня тоже это потрясло. Искать утечки памяти в языке и библиотеке, с которыми НЕ знаком. Вообще.
Записан
JamS007
Гость
« Ответ #13 : Август 25, 2010, 21:00 »

Вся проблема в том, что Вы выделяете память под 300 элементов, они все пустые. Они занимают место в диапазоне 0..299 Потом вы добавляете еще 300 элементов, уже инициализированных значениями, то есть 300 раз выделяете память. Но цикл удаления у вас не правильный, дело в том, что вы начинаете отсчет элементов от 0, а там у вас пустые указатели, соответственно delete нечего не удаляет. А другие 300 элементов так и остаются в памяти.

Если не прав - поправьте.
Записан
sq_vasya
Гость
« Ответ #14 : Август 26, 2010, 00:46 »

const T & QVector::at ( int i ) const
?

Да, уважаемый именно так. Мало того, компилятор не то что ошибку, но даже ворнига не пишет. И это работает.

Меня тоже это потрясло. Искать утечки памяти в языке и библиотеке, с которыми НЕ знаком. Вообще.

Вас это будет потрясать до тех пор, пока Вы не осознаете разницу между указателем на константу и константным указателем.

Перед тем, как отвечать на этот пост, пожалуйста покурите внимательно этот код
Код:
class A
    {
public:
    void NonConstMethod() {}
    };

void main()
    {
    QVector<A*> vectorA;
    A* a = new A;
    vectorA.append( a );
    vectorA.at( 0 )->NonConstMethod();
    delete a;
    }
Возможно, вы будете вновь потрясены, но компилятор примет данный код как вполне себе кошерный и ни в одном месте не выругается.

Далее, предлагаю покурить следующий фрагмент
Код:
class A
    {
public:
    void NonConstMethod() {}
    };

void main()
    {
    const A* a1 = (const A*)0x123123;
    a1 = (const A*) 0x111222;
    // a1->NonConstMethod();

    A* const a2 = (A*) 0x222333;
    // a2 = (A*) 0x444555;
    a2->NonConstMethod();
    }
Если Вы раскоментируете закоментированные строки - компилятор выдаст ошибку. Не поленитесь, вставьте этот код в IDE, скомпилируйте и запустите, возможно так Вы лучше проникнитесь сутью вопроса.

Также рекомендую вдумчиво почитать http://www.cap-design.ru/ccc/5.htm и обратить внимание на разделы "Указатель на константу" и "Константный указатель". Буду рад, если мои выкладки Вам будут полезны.

Далее, прошу не обсуждать этот момент в данном топике, поскольку он его прямо не касается. Если Вам интерестно это обсудить и найти истину, можете создать отдельный топик и там это обсуждать. Я с удовольствием присоединюсь к обсуждению.

Увожу далее разговор от QVector-a. Здесь много было скзано про методы QVector::reserve() и QVector::resize().
Ок, я их добавлял для того чтобы память под элементы выделялась только один раз (да, resize для этого не подходит и там возникает проблема, которую все озвучили несколько раз. внимательный читатель заметил, что я сразу же поправился и стал использовать метод reserve, который соответствует поставленной задаче).
Все. Отказываемся от их использования. Нету их.

Код:
void JustDoIt()
    {
    int objectsAmount = 300;
    QVector<QObject*>* objectsVector = new QVector<QObject*>;

    for ( int i = 0; i < objectsAmount; i++ )
        {
        QObject* object = new QObject;
        objectsVector->append( object );
        qDebug( "objectsVector->size() = %d", objectsVector->size() );
        }

    for ( int i = 0; i < objectsAmount; i++ )
        {
        delete objectsVector->at( i );
        }

    delete objectsVector;
    }

int main(int argc, char *argv[])
    {

    __LEAKS_CHECK_START;

    JustDoIt();

    __LEAKS_CHECK_END;

    return 0;
    }
Компилируем, запускаем и имеем снова ту же проблему "258 memory leaks detected".

Подчёркиваю, нарицаний на QVector у меня нет и использую я его исключительно ради удобства демонстрации проблемы. Я не думаю, что память течёт в векторе - мне не всё ясно с очищением памяти из-под QObject-a.

Увожу разговор от QVector-a совсем
Код:
void JustDoIt()
    {
    QObject* obj1 = new QObject;
    QObject* obj2 = new QObject;
    QObject* obj3 = new QObject;
    QObject* obj4 = new QObject;

    delete obj1;
    delete obj2;
    delete obj3;
    delete obj4;
    }
На выходе имеем "8 memory leaks detected". Т.е. проблема, как мне кажется, где-то с QObject-ом. Или это не проблема, а корректное поведение? Ткните меня носом в мануал, где это написано, а то я что-то не могу понять что происходит.

Напомню, что проверки делаю под Symbian эмулятор и девайс. Просто потому, что знаю как делать замеры выделенной памяти, чтобы искать утечки (методика отработана не на одном проекте и до последнего времени себя оправдывала). Если кто-то знает, как это программно делать на линуксе и на винде (а лучше всего вообще средствами самой Qt) скажите пожалуйста, буду благодарен.
Записан
Страниц: [1] 2 3 ... 5   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.171 секунд. Запросов: 23.