Version: (using KDE KDE 3.4.1) Installed from: Gentoo Packages OS: Linux I've been chasing a memory leak in a couple of my programs now for sometime, and I believe I have found it. Please accept my apologies if I'm missing something obvious here. Bear with me as I try to describe it. I have a few qtruby programs which, after running for a few days, tend to die with OOM errors after growing to very large memory consumption. I also tried manually calling the garbage collector every second while the program was running to no avail. Other programs did not exhibit this behavior, so after some tests, I found out what was causing hte leak: I have a Qt::Object derived class which houses a Qt::Socket that I use for communication with various servers around our building. My Qt::Object class defines this connection: Qt::Object::connect(@socket, SIGNAL('readyRead()'), self, SLOT('MessageReceivedSlot()')) And this slot is defined as: def MessageReceivedSlot emit MessageSignal(@socket.readLine.chomp) while(@socket.canReadLine) end And this is where the memory leak occurs. I made a simple test case to reproduce it. I created a small TCP server which just sends out data over and over again to anything that connects to it: require 'socket' port = 1999.to_i server = TCPServer.new('localhost', port) while (session = server.accept) idx = 0 while true 20.times { session.puts "Request: Blah #{idx}" } idx = idx + 1 sleep 1 end end And then I created a small program which uses this socket class to connect to the server and just continue to receive the messages. I even changed the receive slot to NOT emit the signal, figuring that that may be the problem: def MessageReceivedSlot @socket.readLine.chomp while(@socket.canReadLine) end So all my class does now is continue to receive data, and readLine it, but not do anything with it. I am also calling the garbage collector quite often to make sure I'm not seeing anything funny. Over time, the programs memory consumption continues to grow and grow. It may start out at something like 22000K memory, and after 48 hours will be around 150000K, and will continue until it gets an OOM error. My *hunch* is that there's something spurious going on with string to Qt::String conversion, perhaps a QString is getting created somewhere as a child of my socket and it's never being disposed of, but I don't know for sure. I'm currently running in a debugger in an attempt to see what is causing the continual growth of memory, but any thoughts would be helpful.
As a side note, on another machine where I use this class only for sending data ( using @socket.writeBlock() ), and not receiving, the memory usage does not grow at all. However, it seems that any time I receive a message from a Qt::Socket using the above mentioned methods, the memory usage grows.
If I disconnect my readyRead() signal from the MessageReceivedSlot, and set my socket's readBufferSize to a maximum of 500, and run my test, the program memory does not grow. In other words, if I don't read data out of the socket buffer and let it fill up, the memory consumption stops. However, if I read the data out of the socket, the memory goes up. As an alternative test, I could use the readBlock method, but it takes a char * argument and I haven't figured out yet what to pass to it to get the data. A String doesn't seem to work nor does a Qt::ByteArray.
This seems to work as a workaround: def MessageReceivedSlot bytes = [ ] while true c = @socket.getch break if c == -1 bytes << c end strings = bytes.pack("C*") strings.split("\n").each { |s| emit MessageSignal(s) unless s.empty? } end
The code in comment #3 doesn't work, after 24 hours of running I'm getting a leak again. I can't seem to find anything leaky in the ObjectSpace of ruby itself. I dont' see the number or size of strings, arrays, and hashes getting any bigger. And I don't notice an increase in the number of Qt:: objects. I doubt that the leak is in Qt itself, so perhaps somewhere in the glue code (smoke?) in between the two?
On Wednesday 06 July 2005 14:42, Caleb Tennis wrote: [bugs.kde.org quoted mail] Yes, you're right there is a leak for QString value return types. When a Smoke method returns a QString, it creates a new temporary QString with 'new', but never deletes it. I also tried 'QString*' and 'QString&' return types with readLine(), and those don't create a temporary string. virtual QString readLine(); Generates this code: void x_45(Smoke::Stack x) { // readLine() QString xret = this->QSocket::readLine(); x[0].s_voidp = (void*)new QString(xret); } virtual QString * readLine(); Becomes Generates this code: void x_45(Smoke::Stack x) { // readLine() QString* xret = this->QSocket::readLine(); x[0].s_voidp = (void*)xret; } virtual QString & readLine(); Generates this code: void x_45(Smoke::Stack x) { // readLine() QString& xret = this->QSocket::readLine(); x[0].s_voidp = (void*)&xret; } In Qt.cpp, the class to handle a return value has a boolean method called cleanup(), which is false. class MethodReturnValue : public Marshall { ... bool cleanup() { return false; } }; In handlers.cpp, the code to marshall a QString to a ruby string will only delete the QString if cleanup() is true: case Marshall::ToVALUE: { QString *s = (QString*)m->item().s_voidp; if(s) { if (s->isNull()) { *(m->var()) = Qnil; } else { *(m->var()) = rstringFromQString(s); } if(m->cleanup()) delete s; } else { *(m->var()) = Qnil; } } So the temporary QString from the 'QString readLine()' version of the method is never deleted. Either cleanup() should return true in MethodReturnValue, or the marshalling code should check if the QString is a value type and only delete it if it is. I looked at the PerlQt code in PerlQt-3.009 beta and it's just the same, and will have this leak too. -- Richard
SVN commit 433005 by rdale: 2005-07-09 Richard Dale <Richard_Dale@tipitina.demon.co.uk> * When a Qt method returned a QString value type, such as: QString QSocket::readLine() A temporary QString was being created that wasn't deleted and caused a memory leak. Fixes problem reported by Caleb Tennis. CCBUGS:108650 M +7 -0 ChangeLog M +1 -1 rubylib/qtruby/handlers.cpp --- trunk/KDE/kdebindings/qtruby/ChangeLog #433004:433005 @@ -1,3 +1,10 @@ +2005-07-09 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * When a Qt method returned a QString value type, such as: + QString QSocket::readLine() + A temporary QString was being created that wasn't deleted and caused a + memory leak. Fixes problem reported by Caleb Tennis. + 2005-06-28 Richard Dale <Richard_Dale@tipitina.demon.co.uk> * Improved display of Qt::Enums in the KDevelop debugger. With a p --- trunk/KDE/kdebindings/qtruby/rubylib/qtruby/handlers.cpp #433004:433005 @@ -863,7 +863,7 @@ } else { *(m->var()) = rstringFromQString(s); } - if(m->cleanup()) + if(m->cleanup() || m->type().isStack()) delete s; } else { *(m->var()) = Qnil;
I've patched my source, recompiled, but now it dies: tc@supercell ~/subversion/ruby/unico $ ruby unico.rb /usr/lib/ruby/site_ruby/1.8/gina/client.rb:76: [BUG] Segmentation fault ruby 1.8.2 (2004-12-25) [i686-linux] Aborted (Line 76 is my line where I call a method that returns a QString). Removing the patch and recompiling, the program runs fine again, but with the memory leak.
SVN commit 434490 by rdale: * Added example programs for client/server programming with Qt::Socket and associated classes. client.rb illustrates current bugs in QtRuby * Qt::Socket.canReadLine() always returns true * Qt::readLine() seg faults when called a second time * A memory leak and seg faulting problems like the above were reported by Caleb Tennis CCBUGS: 108650 M +9 -0 ChangeLog A rubylib/examples/network (directory) A rubylib/examples/network/clientserver (directory) A rubylib/examples/network/clientserver/client (directory) A rubylib/examples/network/clientserver/client/client.rb A rubylib/examples/network/clientserver/server (directory) A rubylib/examples/network/clientserver/server/server.rb --- trunk/KDE/kdebindings/qtruby/ChangeLog #434489:434490 @@ -1,3 +1,12 @@ +2005-07-14 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Added example programs for client/server programming with Qt::Socket + and associated classes. client.rb illustrates current bugs in QtRuby + * Qt::Socket.canReadLine() always returns true + * Qt::readLine() seg faults when called a second time + * A memory leak and seg faulting problems like the above were reported + by Caleb Tennis + 2005-07-09 Richard Dale <Richard_Dale@tipitina.demon.co.uk> * When a Qt method returned a QString value type, such as:
SVN commit 434751 by rdale: * Qt::Socket started working correctly when I and regenerated and rebuilt my Smoke library. Before then it was calling the wrong version of QSocket::at() for some reason, and wasn't discarding bytes that had already been read. * Removed comment from the client.rb example about Qt::Socket.canReadLine always returning true now it works. CCBUGS: 108650 M +9 -0 ChangeLog M +0 -2 rubylib/examples/network/clientserver/client/client.rb --- trunk/KDE/kdebindings/qtruby/ChangeLog #434750:434751 @@ -1,3 +1,12 @@ +2005-07-15 Richard Dale <Richard_Dale@tipitina.demon.co.uk> + + * Qt::Socket started working correctly when I and regenerated and rebuilt + my Smoke library. Before then it was calling the wrong version of + QSocket::at() for some reason, and wasn't discarding bytes that had + already been read. + * Removed comment from the client.rb example about Qt::Socket.canReadLine + always returning true now it works. + 2005-07-14 Richard Dale <Richard_Dale@tipitina.demon.co.uk> * Added example programs for client/server programming with Qt::Socket --- trunk/KDE/kdebindings/qtruby/rubylib/examples/network/clientserver/client/client.rb #434750:434751 @@ -59,8 +59,6 @@ def socketReadyRead() # read from the server - # This loops forever in QtRuby because 'canReadLine()' - # is alway true while @socket.canReadLine() do @infoText.append( @socket.readLine() ) end
Thank you for the bug report. As this report hasn't seen any changes in 5 years or more, we ask if you can please confirm that the issue still persists. If this bug is no longer persisting or relevant please change the status to resolved.
QtRuby only existed for Qt4 and Korundum, which never got updated for 5.x This is unmaintained and any effort towards QtRuby for Qt6 will be from scratch. Therefore, closing this ticket