Использование памяти увеличивается со временем при записи в файл

#c #qt

#c #qt

Вопрос:

У меня есть небольшая программа на C на основе Qt, и эта программа просто записывает вектор в файл, пока я не нажму кнопку «СТОП».

В Qt designer я просто нажимаю кнопку, меняю текст на «STOP» и называю объект «button».

Проблема в том, что когда я проверяю использование памяти программой с помощью команды watch в терминале, я вижу, что свободная память со временем уменьшается, и нажатие на кнопку на это не влияет. Оно возвращается к своему первоначальному состоянию, только если я закрываю приложение.

test.pro :

 #-------------------------------------------------
#
# Project created by QtCreator 2016-10-12T13:56:45
#
#-------------------------------------------------

QT        = core gui

greaterThan(QT_MAJOR_VERSION, 4): QT  = widgets

QMAKE_CXXFLAGS  = -std=c  11
QMAKE_CXXFLAGS  = -D_GLIBCXX_USE_CXX11_ABI=0
QMAKE_CXXFLAGS  = -pthread

TARGET = Test
TEMPLATE = app


SOURCES  = main.cpp
        mainwindow.cpp 
    writer.cpp

HEADERS   = mainwindow.h 
    writer.h

FORMS     = mainwindow.ui
  

mainwindow.h :

 #ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

#include <writer.h>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
    void on_stopButton_clicked();

    void on_startButton_clicked();

private:
    Ui::MainWindow *ui;
    writer *w;
};

#endif // MAINWINDOW_H
  

writer.h :

 #ifndef WRITER_H
#define WRITER_H

#include <iostream>
#include <fstream>
#include <iterator>
#include <string>
#include <locale>
#include <sstream>
#include <thread>
#include <atomic>
#include <vector>

#define COUNTER_FILENAME "data_counter.txt"
#define DATA_PREFIX "data_no"
#define DATA_EXTENSION ".csv"

class writer
{
public:
    // For labeling
    unsigned int label_id;
    bool label;
    // Start writing frame features in a file
    void write();
    // Stop writing
    void stopWriting();
    // Default demo constructor, sets up the frame thread which will process frame data in a seperate thread
    explicit writer();
    // Default demo destructor, flags that the frame thread should stop and waits for the frame thread to join
    ~writer();

protected:
    std::unique_ptr< std::thread > frameThread;
    std::atomic< bool > stopFrameThread;
    std::vector< float > frameFeatures;
    std::string data_filename;
};

#endif // WRITER_H
  

main.cpp :

 #include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}
  

mainwindow.cpp :

 #include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow),
    w(new writer())
{
    ui->setupUi(this);


}

MainWindow::~MainWindow()
{
    delete w;
    delete ui;
}

void MainWindow::on_stopButton_clicked()
{
    w->stopWriting();
}

void MainWindow::on_startButton_clicked()
{
    w->write();
}
  

writer.cpp :

 #include "writer.h"

writer::writer() :
    label_id( 0 ),
    label( false )
{
    stopFrameThread = false;
    // Resize the vector to put all frame features
    frameFeatures.resize( 1743, 0.0 );
}

writer::~writer()
{
    stopWriting();
}

void writer::write()
{
    // Setup the frame thread
    stopFrameThread = false;

    // Create a new thread to retrieve all features of each frame
    frameThread = std::unique_ptr< std::thread >( new std::thread( [ this ](){
        // Read from counter file to get counter value
        std::string counter;
        std::ifstream counter_in (COUNTER_FILENAME);
        if( counter_in.is_open() ){
            std::getline( counter_in, counter );
            counter_in.close();
        }else{
            counter = "0";
        }

        // Convert the counter into an integer
        int c;
        std::stringstream( counter ) >> c;

        // Compute data filename
        data_filename = DATA_PREFIX   counter   "_id"   applicant_id   DATA_EXTENSION;

        std::ofstream data_file( data_filename );

        if( data_file.is_open() ){
            // Start the main processing loop
            while( ! stopFrameThread ){
                for(int index = 0; index < 1743; index  ){
                    frameFeatures[ index ] = index;
                }

                for(std::vector< float >::iterator it = frameFeatures.begin(); it != frameFeatures.end();   it){
                    data_file << " "   std::to_string( *it );
                }
                data_file << "n";
            }

            // Close the data file
            data_file.close();

            // Write incremented counter value on counter file
            std::ofstream counter_out (COUNTER_FILENAME);
            if( counter_out.is_open() ){
                counter_out <<   c;
                counter_out.close();
            }else{
                std::cerr << "Unable to write on counter filen";
            }
        }else{
            std::cerr << "Unable to open the data filen";
        }
    } ) );
}

void writer::stopWriting()
{
    // Flag that the frame thread should stop
    stopFrameThread = true;

    // Wait for the frame thread to stop
    if( frameThread ){
        if( frameThread->joinable() ){
            frameThread->join();
        }
    }
}
  

Спасибо за любую помощь.

Комментарии:

1. Проблема в том, что когда я проверяю использование памяти программой с помощью команды watch в терминале — вы не должны использовать инструменты ОС, чтобы определить, действительно ли ваша программа на C пропускает память. Диспетчер кучи среды выполнения C может удерживать память на случай, если потребуются дополнительные выделения. Добавьте кнопку «Пуск» и посмотрите, продолжает ли увеличиваться объем памяти при повторной записи вектора. Если объем памяти не увеличивается, это подтверждает, что диспетчер кучи работает так, как задумано, и явной утечки памяти нет.

2. Вы имеете в виду добавить кнопку для запуска потока вместо запуска его с помощью конструктора?

3. Нет, я имею в виду запуск, остановку, а затем повторный запуск в том же запуске вашего приложения. Это второй запуск, который определит, действительно ли у вас утечка памяти. Если вы видите, что объем памяти начинает увеличиваться с той же скоростью, что и при первоначальном запуске, то, вероятно, вы можете привести доводы в пользу утечки памяти. Если вы не видите такого увеличения, то это подтверждает, что диспетчер кучи выполняет свою работу.

4. Я сделал, как вы сказали, и когда я останавливаю запуск, использование памяти остается на том же уровне, но когда я (повторно) запускаю, оно продолжает увеличиваться: ( Итак, это означает, что у меня утечка памяти, не так ли? Откуда это может быть?

Ответ №1:

Прошло некоторое время с тех пор, как я исправил проблему, но я подумал, что должен написать это здесь на случай, если кто-то столкнется с подобной проблемой.

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

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