Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Memoy leak using QtCharts



  • Hello everyone,

    I hope you are haveing a nice day,

    I am currently trying to plot indefinitely a graph on a chartview.

    I have memory leak that I can't find the origine.

    Here is a quiet simple code to illustrate this fact :

    void MyChartView::Draw(void)
    {
    	int Length = 10000;
    	double Frequency = 0.1;
    	chart()->removeAllSeries(); //here I remove the series
    	QLineSeries *SerieToAdd = new QLineSeries;
    	for (int i = 0; i < Length; i++)
    	{
    		SerieToAdd->append(i, sin(static_cast<double>(i) * 3.14*2*0.1)); 
    	}
    	chart()->addSeries(SerieToAdd); //here I add the serie
    }
    

    for information, in this example, MyChartView is a basic class, nothing really creazy:

    class MyChartView:public QChartView
    {
    public:
    	MyChartView();
    	~MyChartView();
    	void Draw(void);
    };
    

    If I launch 10000 times the function Draw like below, I will see a memory leak.

    0_1540814669924_Chartview.PNG

    Do you know what I am doing wrong?


  • Qt Champions 2020

    Do you know what I am doing wrong?

    Just don't use QtCharts... Use, e.g Qwt.


  • Lifetime Qt Champion

    Hi
    How big is this leak ?
    Could you try to do
    MyChartView test; ( as in no new.ing)
    Currently the test is not deleted as you assign no parent.



  • @kuzulis : hahahhabut by the way. I want to understand here why it doesn't work in this case!

    @mrjj : something around 10ko for each time when Draw is used.

    There is a strange thing. If I code the code below :

    void MyChartView::Draw(void)
    {
    	int Length = 10000;
    	double Frequency = 0.1;
    
    	chart()->removeAllSeries(); //here I remove the series
    	QLineSeries *SerieToAdd = new QLineSeries;
    	for (int i = 0; i < Length; i++)
    	{
    		SerieToAdd->append(i, sin(static_cast<double>(i) * 3.14*2*0.1)); 
                              //or whatever
    	}
    
    	//chart()->addSeries(SerieToAdd); //here I comment this line
    
    	delete SerieToAdd;
    
    }
    

    I don't have any memoy leak. But, if I wrote the same code but with the addSeries line, the leak appear... (here below)

    void MyChartView::Draw(void)
    {
    	int Length = 10000;
    	double Frequency = 0.1;
    
    	chart()->removeAllSeries(); //here I remove the series
    	QLineSeries *SerieToAdd = new QLineSeries;
    	for (int i = 0; i < Length; i++)
    	{
    		SerieToAdd->append(i, sin(static_cast<double>(i) * 3.14*2*0.1));
     //or whatever
    	}
    	chart()->addSeries(SerieToAdd); //here I add the serie!!!!!!!LEAK
    	delete SerieToAdd;
    }
    

    This is quiet strange because I am supposed to remove and to delete the series that I have added to the chart at each iteration with the command removeAllSeries() no? So normally the size of my object must be constant in time...

    Could you try to do MyChartView test; ( as in no new.ing) I am sorry i m not sure that I have understand what you asked me...

    Currently the test is not deleted as you assign no parent. Yes! I don't want to delete the test, I just want that its size remains constant in time...


  • Lifetime Qt Champion

    Hi

    • Currently the test is not deleted as you assign no parent. Yes! I don't want to delete the test, I just want that its size remains constant in time...

    I understand. i was just wondering if the lost memory was due to some house keeping in ChartView
    that would only be freed on app exit./ Widget destruction.

    However, it does seem odd as removeAllSeries says it will delete it and
    addSeries says it takes ownership so all should be fine.

    Could you try rem out
    // SerieToAdd->append(..
    and see if mem use is affected. Then we know its directly related to that.


  • Lifetime Qt Champion

    A small, compilable testcase would be the best here so we can check it (and maybe at it to the bugreport later on) :)


  • Qt Champions 2020

    @aawawa said in Memoy leak using QtCharts:

    hahahhabut by the way. I want to understand here why it doesn't work in this case!

    I'm too tried to use QtCharts and too faced with a memory leaks and etc.. I spat on QtCharts and replaced it with Qwt. (Because I haven't time to understand why QtCharts does not work as expected.. This was in Qt 5.9, as I remember).


  • Qt Champions 2017

    @kuzulis said in Memoy leak using QtCharts:

    I spat on QtCharts and replaced it with Qwt. (Because I haven't time to unterstand why QtCharts does not work as expected.. This was in Qt 5.9, as I remember).

    It hasn't improved since then. I did the same as you but picked QCustomPlot instead.



  • @Christian-Ehrlicher : please find a micro project here : link text

    @mrjj : if I comment as this :

    //SerieToAdd->append(i, sin(static_cast<double>(i) * 3.14*2
    

    I have always the memory leak but it seems to be less important (around 6ko)


  • Lifetime Qt Champion

    @aawawa
    Ok so it does seems related to the series.
    If you want to dig into it, you can very handy browse code here
    https://code.woboq.org/qt5/

    Super with sample.



  • @mrjj I 'm going to take a look to your link. Is it a database or you want me to send my code there?


  • Lifetime Qt Champion

    @aawawa
    Hi
    Its a code browser. it makes it really easy to see what is being used. all call to functions are links and
    its almost as good as the debugger. at least to see what is going on.
    I was thinking you would go and look how it deletes the series or something like that.
    The sample is for Christian-Ehrlicher as he might make it into a bug report if we can
    reproduce it and find no good reason for not release the mem on RemoveAllSeries.


  • Lifetime Qt Champion

    I can see some kind of 'leak' but need to do some more investigations.


  • Lifetime Qt Champion

    Ok, no leak there. Just a lot of data. You're creating 100000 QLineSeries with 10000 each - this will need some memory and time. I would rather try to add the points to one QLineSeries.



  • @Christian-Ehrlicher thank you... But it is supposed to be deleted 100000 times in my case no when I use RemoveAllSeries no?



  • @Christian-Ehrlicher would rather try to add the points to one QLineSeries this strategy works quiet well.
    By changing y program as this for the header :

    class MyChartView:public QChartView
    {
    public:
    	MyChartView();
    	~MyChartView();
    	void Draw(void);
    
    private:	
    	QLineSeries *mSeries;
    };
    

    and for the .cpp

    MyChartView::MyChartView()
    {
    
    	mSeries = new QLineSeries;
    	chart()->addSeries(mSeries);
    
    }
    
    
    MyChartView::~MyChartView()
    {
    }
    
    void MyChartView::Draw(void)
    {
    	int Length = 1000;
    	mSeries->clear();
    	for (int i = 0; i < Length; i++)
    	{
    		mSeries->append(QPoint (i, sin(2 * i*3.141516*0.1)));
    	}
    	//update();
    
    }
    
    

    It seems (I have to confirm this point later) to not have anymore memory leak


  • Lifetime Qt Champion

    @aawawa said in Memoy leak using QtCharts:

    But it is supposed to be deleted 100000 times in my case no when I use RemoveAllSeries no?

    You're right. I think I found the culprit:

    qlineseries.cpp:112
    
    QLineSeries::QLineSeries(QObject *parent)
        : QXYSeries(*new QLineSeriesPrivate(this), parent)
    

    this is never deleted... :/



  • @Christian-Ehrlicher
    I think this is the second time I've seen that *new cause this. When will people decide that C++ is getting too clever for its boots? ;-)


  • Lifetime Qt Champion

    Hi,

    @Christian-Ehrlicher said in Memoy leak using QtCharts:

    QLineSeriesPrivate

    It's QObject based, so when the QXYSeries object is deleted it should also get deleted.

    It's indeed QObject based but stored in a QScopedPointer so it will automatically get delete on object's destruction.



  • @Christian-Ehrlicher Thank you. It's the first time I see *new in c++ what does it mean?


  • Lifetime Qt Champion

    @aawawa
    Hi
    Its a Dereference
    http://www.cplusplus.com/doc/tutorial/pointers/
    See section Dereference operator (*)



  • @mrjj yes... I understand... But I don't understand the location of the asterix (*). I am used for example to write

    int *a = new int;
    

    what does

    *new int
    

    means?


  • Lifetime Qt Champion

    @aawawa
    Means
    value pointed to by ptr
    The actual value.

    int copy = *SomeIntPtr;
    that will copy the value to the copy var.
    if you did
    int copy = SomeIntPtr;
    you get the address of pointer into copy


  • Lifetime Qt Champion

    Ok, no memleak at all. You testcase is wrong / inaccurate.
    The problem with your testcase is that your eventloop is not (yet) running and that you also call it in an loop without a chance for the eventloop to do it's work. Since the internals of QLineSeries is cleaned up with a deferred delete it is not cleaned up until the eventloop is called (and after app.exec()). That's also the reason I thought the dtor is not called at all.



  • @Christian-Ehrlicher said in Memoy leak using QtCharts:

    Ok, no memleak at all. You testcase is wrong / inaccurate.
    The problem with your testcase is that your eventloop is not (yet) running and that you also call it in an loop without a chance for the eventloop to do it's work. Since the internals of QLineSeries is cleaned up with a deferred delete it is not cleaned up until the eventloop is called (and after app.exec()). That's also the reason I thought the dtor is not called at all.

    It seems to me that there are some memory leaks happening here.

    This code increases memory usage continuously:

    aSeries->remove(aSeries->points().size() - 1);
    aSeries->append(timestamp, mean);

    Bit this one does not:

    aSeries->replace(aSeries->points().size() - 1, timestamp, mean);

    I'm using Qt 5.11.2.



  • @JosuGZ
    In the last post 4 months ago made by @Christian-Ehrlicher, he explained that memory would only be recovered (and hence [hopefully] not permanently grow) when the OP allowed his code to reach the main event loop. I don't know how replace works, but remove surely will do a deferred delete. So in your example you need to be clear/show us how your code hits the event loop each time after do the remove/append?


  • Moderators

    hi @JosuGZ and welcome

    if you only monitor the memory your OS gives your application, then that is inaccurate.

    You have no influence over when the os decides that your freed memory will no longer be reserved for your application.

    Therefore
    remove -> append is memory is "freed" and a new allocation happens
    replace -> new data overwrites old data



  • @J.Hilk said in Memoy leak using QtCharts:

    hi @JosuGZ and welcome

    if you only monitor the memory your OS gives your application, then that is inaccurate.

    You have no influence over when the os decides that your freed memory will no longer be reserved for your application.

    Therefore
    remove -> append is memory is "freed" and a new allocation happens
    replace -> new data overwrites old data

    An order of magnitude more RAM needed for a simple app is not an OS problem.

    Two tests more replacing the offending line, one with small allocations:

    char *test = (char *)malloc(10);
    test[5] = 't'; // Touching memory
    aSeries->replace(aSeries->points().size() - 1, ms, mean);
    free(test);
    

    Another with bigger allocations:

    char *test = (char *)malloc(10000);
    test[5000] = 't'; // Touching memory
    aSeries->replace(aSeries->points().size() - 1, ms, mean);
    free(test);
    

    Both keep memory usage low as expected.

    Even this keeps the memory constant:

    QWidget *test = new QWidget;
    aSeries->replace(aSeries->points().size() - 1, ms, mean);
    test->deleteLater();
    

    Something is happening with this library and I'm not the only one with issues.


Log in to reply