digitalmars.D.learn - how to avoid memory leaking
- maarten van damme (16/16) May 10 2013 I'm trying to generate images and after displaying them I want to save t...
- Benjamin Thaut (12/29) May 12 2013 Try allocating the big array with:
- maarten van damme (9/46) May 12 2013 I tried using gc.malloc and gc.free but got an invalidmemoryoperation
- Benjamin Thaut (6/76) May 12 2013 Calling GC.free should not be neccessary as the data will still
- maarten van damme (9/9) May 12 2013 This is ridiculous. Your solution appears to keep the memory somewhat
- bearophile (4/9) May 12 2013 Once minimized, this may be fit for D Bugzilla.
- Benjamin Thaut (10/19) May 12 2013 As D is a relative new lagnauge stuff like this can happen. It would be
- maarten van damme (29/51) May 12 2013 aren't you guys all getting tons of internal error's as soon as you comb...
- Benjamin Thaut (13/13) May 12 2013 Instead of the .dup you can use the slice operator []. Although this
- maarten van damme (8/21) May 13 2013 But seeing as the memory only starts growing when I put the png line in,
- Adam D. Ruppe (42/44) May 13 2013 This is pretty strange because looking at the source there, the
- Adam D. Ruppe (33/33) May 13 2013 actually the output array doesn't need to be there at all, I
- Maxim Fomin (15/41) May 13 2013 1) your code is not compilable on 64x system due to erroneous
- maarten van damme (29/66) May 13 2013 I'll check every uint occurance, thank you. I'm on windows so I won't ge...
- Adam D. Ruppe (27/32) May 13 2013 I won't be on my linux box with free time until at least tomorrow
- maarten van damme (4/34) May 14 2013 So now pgen.d works (without simpledisplay version an reusing trueimage)...
I'm trying to generate images and after displaying them I want to save them to a png file. I use parallelism to generate them asynchronously with the gui-thread to avoid blocking (don't know if this is really relevant). If I add a single line in the method where I generate the image that creates a class which allocates a pretty big array, slowly but surely the memory starts to rise until it ends in a spectacular out of memory exception. I always thought the garbage collector would deal with it. I tried reducing it but failed. Maybe I should let dustmite bite in it a bit later. How can I manually fix this? Is this a known bug in the garbage collector? btw, this code also caused segfaults on my 64 bit laptop on linux. How can I avoid this? main class: https://dl.dropboxusercontent.com/u/15024434/picturegenerator/generalised.d zip file with everything ready to go to compile: https://dl.dropboxusercontent.com/u/15024434/picturegenerator/picturegenerator.zip
May 10 2013
Am 11.05.2013 01:18, schrieb maarten van damme:I'm trying to generate images and after displaying them I want to save them to a png file. I use parallelism to generate them asynchronously with the gui-thread to avoid blocking (don't know if this is really relevant). If I add a single line in the method where I generate the image that creates a class which allocates a pretty big array, slowly but surely the memory starts to rise until it ends in a spectacular out of memory exception. I always thought the garbage collector would deal with it. I tried reducing it but failed. Maybe I should let dustmite bite in it a bit later. How can I manually fix this? Is this a known bug in the garbage collector? btw, this code also caused segfaults on my 64 bit laptop on linux. How can I avoid this? main class: https://dl.dropboxusercontent.com/u/15024434/picturegenerator/generalised.d zip file with everything ready to go to compile: https://dl.dropboxusercontent.com/u/15024434/picturegenerator/picturegenerator.zipTry allocating the big array with: import core.memory; size_t imageSize = 1024*1024*3; auto imageData[] = (cast(ubyte*)GC.malloc(imageSize , GC.BlkAttr.NO_SCAN)[0..imageSize]; Which will result in the big memory block not beeing scanned by the GC which should reduce the number of false positives during collection. If this doesn't work, use malloc / free of the c standard library to allocate and free the image data. Kind Regards Benjamin Thaut
May 12 2013
I tried using gc.malloc and gc.free but got an invalidmemoryoperation exception when doing gc.free(data.ptr); Simply using gc.malloc like you proposed, the memory keeps growing. Is this a bug in the d garbage collector? What's really strange though, is that the destructor of trueimagecolor (the class allocating the buffer) is getting called everytime... Does this mean that a destructor getting called still is no garantee that all data is getting free'd? 2013/5/12 Benjamin Thaut <code benjamin-thaut.de>Am 11.05.2013 01:18, schrieb maarten van damme: I'm trying to generate images and after displaying them I want to savethem to a png file. I use parallelism to generate them asynchronously with the gui-thread to avoid blocking (don't know if this is really relevant). If I add a single line in the method where I generate the image that creates a class which allocates a pretty big array, slowly but surely the memory starts to rise until it ends in a spectacular out of memory exception. I always thought the garbage collector would deal with it. I tried reducing it but failed. Maybe I should let dustmite bite in it a bit later. How can I manually fix this? Is this a known bug in the garbage collector? btw, this code also caused segfaults on my 64 bit laptop on linux. How can I avoid this? main class: https://dl.dropboxusercontent.**com/u/15024434/** picturegenerator/generalised.d<https://dl.dropboxusercontent.com/u/15024434/picturegenerator/generalised.d> zip file with everything ready to go to compile: https://dl.dropboxusercontent.**com/u/15024434/**picturegenerator/** picturegenerator.zip<https://dl.dropboxusercontent.com/u/15024434/picturegenerator/picturegenerator.zip>Try allocating the big array with: import core.memory; size_t imageSize = 1024*1024*3; auto imageData[] = (cast(ubyte*)GC.malloc(**imageSize , GC.BlkAttr.NO_SCAN)[0..**imageSize]; Which will result in the big memory block not beeing scanned by the GC which should reduce the number of false positives during collection. If this doesn't work, use malloc / free of the c standard library to allocate and free the image data. Kind Regards Benjamin Thaut
May 12 2013
On Sunday, 12 May 2013 at 11:27:15 UTC, maarten van damme wrote:I tried using gc.malloc and gc.free but got an invalidmemoryoperation exception when doing gc.free(data.ptr); Simply using gc.malloc like you proposed, the memory keeps growing. Is this a bug in the d garbage collector? What's really strange though, is that the destructor of trueimagecolor (the class allocating the buffer) is getting called everytime... Does this mean that a destructor getting called still is no garantee that all data is getting free'd?Calling GC.free should not be neccessary as the data will still be collected. The destructor of TrueImageColor beeing called does not mean the Array with the Image Data gets collected. Are you Sure that TrueColorImage is the only class holding a reference to the data? Do you take a slice of the data somewhere?2013/5/12 Benjamin Thaut <code benjamin-thaut.de>Am 11.05.2013 01:18, schrieb maarten van damme: I'm trying to generate images and after displaying them I want to savethem to a png file. I use parallelism to generate them asynchronously with the gui-thread to avoid blocking (don't know if this is really relevant). If I add a single line in the method where I generate the image that creates a class which allocates a pretty big array, slowly but surely the memory starts to rise until it ends in a spectacular out of memory exception. I always thought the garbage collector would deal with it. I tried reducing it but failed. Maybe I should let dustmite bite in it a bit later. How can I manually fix this? Is this a known bug in the garbage collector? btw, this code also caused segfaults on my 64 bit laptop on linux. How can I avoid this? main class: https://dl.dropboxusercontent.**com/u/15024434/** picturegenerator/generalised.d<https://dl.dropboxusercontent.com/u/15024434/picturegenerator/generalised.d> zip file with everything ready to go to compile: https://dl.dropboxusercontent.**com/u/15024434/**picturegenerator/** picturegenerator.zip<https://dl.dropboxusercontent.com/u/15024434/picturegenerator/picturegenerator.zip>Try allocating the big array with: import core.memory; size_t imageSize = 1024*1024*3; auto imageData[] = (cast(ubyte*)GC.malloc(**imageSize , GC.BlkAttr.NO_SCAN)[0..**imageSize]; Which will result in the big memory block not beeing scanned by the GC which should reduce the number of false positives during collection. If this doesn't work, use malloc / free of the c standard library to allocate and free the image data. Kind Regards Benjamin Thaut
May 12 2013
This is ridiculous. Your solution appears to keep the memory somewhat constant around 20mb-28mb untill it suddenly runs out of memory. Literally out of nowhere. I have no idea what's causing everything, so I decided to get rid of the window altogether and to try saving the images instead of displaying + saving them. Now I run in "Internal error: ..\ztc\cgcs.c 343"... D is really dissapointing here. Maybe it's the gc having fun, maybe it's something else, no way to know for sure. Having to give up on displaying it altogether runs into internal error's.
May 12 2013
maarten van damme:I have no idea what's causing everything, so I decided to get rid of the window altogether and to try saving the images instead of displaying + saving them. Now I run in "Internal error: ..\ztc\cgcs.c 343"...Once minimized, this may be fit for D Bugzilla. Bye, bearophile
May 12 2013
Am 12.05.2013 21:05, schrieb maarten van damme:This is ridiculous. Your solution appears to keep the memory somewhat constant around 20mb-28mb untill it suddenly runs out of memory. Literally out of nowhere. I have no idea what's causing everything, so I decided to get rid of the window altogether and to try saving the images instead of displaying + saving them. Now I run in "Internal error: ..\ztc\cgcs.c 343"... D is really dissapointing here. Maybe it's the gc having fun, maybe it's something else, no way to know for sure. Having to give up on displaying it altogether runs into internal error's.As D is a relative new lagnauge stuff like this can happen. It would be great if you could reduce the compiler crash to a minimal test case using dustmite: https://github.com/CyberShadow/DustMite A description how to use it is aviable in the wiki. If you successfully reduced the compiler error please fill in a bug report at http://d.puremagic.com/issues/ -- Kind Regards Benjamin Thaut
May 12 2013
aren't you guys all getting tons of internal error's as soon as you combine dynamic arrays with static arrays? It seems as if these things are completely seperate things, but their syntax sugests a more closely related connection. I really find it confusing... So, after reducing, I am very certain that at least one problem comes from generating a png, the code used is here available : https://github.com/adamdruppe/misc-stuff-including-D-programming-language-web-stuff/blob/master/png.d seeing as this is pure d, am I right in assuming there should never be any memory leaks when using this? The reduced loop that only generates images is here : TrueColorImage i= new TrueColorImage(width,height); PNG* png; double[][4] curtrans; while(true){ curtrans=generateTransformationMatrix(); for(int y=0;y<height;y++) for(int x=0;x<width;x++) i.data[(y*width+x)*4..y*width+x)*4+4]=colorify(applyTransformation(transformXY(x,y),curtrans)).dup[0..3] ~ 255; // and finally write the data to a png file png = pngFromImage(i); //std.file.write("images/"~toHexString(md5Of(curtrans))~".png", writePng(png)); } if you comment out "png = pngFromImage(i)" the program appears to not blow up over time. I think the ice come from assiging a slice of a dynamic array to a slice of a static array (hence the .dup). (I'll try reducing it with dustmite) 2013/5/12 Benjamin Thaut <code benjamin-thaut.de>Am 12.05.2013 21:05, schrieb maarten van damme:This is ridiculous. Your solution appears to keep the memory somewhat constant around 20mb-28mb untill it suddenly runs out of memory. Literally out of nowhere. I have no idea what's causing everything, so I decided to get rid of the window altogether and to try saving the images instead of displaying + saving them. Now I run in "Internal error: ..\ztc\cgcs.c 343"... D is really dissapointing here. Maybe it's the gc having fun, maybe it's something else, no way to know for sure. Having to give up on displaying it altogether runs into internal error's.As D is a relative new lagnauge stuff like this can happen. It would be great if you could reduce the compiler crash to a minimal test case using dustmite: https://github.com/**CyberShadow/DustMite<https://github.com/CyberShadow/DustMite> A description how to use it is aviable in the wiki. If you successfully reduced the compiler error please fill in a bug report at http://d.puremagic.com/issues/ -- Kind Regards Benjamin Thaut
May 12 2013
Instead of the .dup you can use the slice operator []. Although this really should not cause a compiler crash if you forgett to do so. int[3] staticArray; int[] normalArray = staticArray[]; I'm not seeing something fundamentally wrong with your code right now. I will most likely have some more time to look at it tomorrow. Does it use any plattform specific features? (E.g. does it work on windows?) Having a GC does not mean you can not have memory leaks. If you manage to keep the memory referenced by some hard rooted reference the GC is not allowed to free it and you will get a memory leak. I'm also currently not aware of any tools for D that will help you find memory leaks. Kind Regards Benjamin Thaut
May 12 2013
But seeing as the memory only starts growing when I put the png line in, and that I keep deleting the old class and replacing it for a new instance, shouldn't the gc clean up everything as nothing can still be referencing to that data? I am working on windows, on linux my original code segfaulted, I don't know about the reduced version though. Afaik it doesn't use a platform dependent feature. 2013/5/13 Benjamin Thaut <code benjamin-thaut.de>Instead of the .dup you can use the slice operator []. Although this really should not cause a compiler crash if you forgett to do so. int[3] staticArray; int[] normalArray = staticArray[]; I'm not seeing something fundamentally wrong with your code right now. I will most likely have some more time to look at it tomorrow. Does it use any plattform specific features? (E.g. does it work on windows?) Having a GC does not mean you can not have memory leaks. If you manage to keep the memory referenced by some hard rooted reference the GC is not allowed to free it and you will get a memory leak. I'm also currently not aware of any tools for D that will help you find memory leaks. Kind Regards Benjamin Thaut
May 13 2013
On Monday, 13 May 2013 at 07:13:03 UTC, maarten van damme wrote:But seeing as the memory only starts growing when I put the png line inThis is pretty strange because looking at the source there, the only time data is even referenced is here: output ~= data[pos..pos+bytesPerLine]; Which makes a copy. There's no delegates or anything so shouldn't be a copied stack reference sticking around.... I see no reason why this wouldn't work. I also tried replacing the new ubyte with malloc/free hoping it'd get an access violation if it was used after free, and nothing came up. But my computer that i'm using right now is one of those netbooks so it doesn't have the cpu power to generate hte large images. I did a 200x100 instead which obviously uses way less memory. But still, if it was a bad reference I think it would show up here too. The only other line that might be a problem is this one: auto com = cast(ubyte[]) compress(output); output is at this point a copy of data, with other things added. So it'd be a very large array too.... and is created through the ~= operator, so it is all gc managed. Could be a false positive pointing into this array causing the leak. And the compress function, from std.zlib, does this: auto destlen = srcbuf.length + ((srcbuf.length + 1023) / 1024) + 12; auto destbuf = new ubyte[destlen]; auto err = etc.c.zlib.compress2(destbuf.ptr, &destlen, cast(ubyte *)srcbuf.ptr, srcbuf.length, level); if (err) { delete destbuf; throw new ZlibException(err); } destbuf.length = destlen; Which is again, destbuf being another large gc managed array. So my guess is either my output or std.zlib's destbuf is getting mistaken for still being referenced by some random number on the stack, so the gc is conservatively keeping it around. A potential fix would be for my png.d to manually manage this memory - preallocate output[] with malloc and perhaps use its own compress function with its own manual buffer too. I figure this would be a new function, savePngFromImage, that saves the file itself instead of returning the array so it has complete control over the reference.
May 13 2013
actually the output array doesn't need to be there at all, I could compress on the fly, adding the filter byte to a much smaller buffer, then writing straight out to the file. Then the only big array would be the one in the image class itself, which is easy to manage. In fact, I did another file originally called imagedraft.d that does pngs as lazy ranges. Doesn't look like I ever posted that one to github. I think it can actually read more png files too. But in any case, switching to that will be harder since I don't even remember how to use it. ANYWAY, I added a new function to png.d: void writeImageToPngFile(in char[] filename, TrueColorImage image); Use it like this: // and finally write the data to a png file writeImageToPngFile("images/"~toHexString(md5Of(curtrans))~".png", i); And see if things are any better. This one uses malloc/free for its temporary buffer and uses the Compress class from std.zlib to do it one line at a time instead of the big function, so hopefully its temporary buffer will be more manageable too. It also explicitly deletes the compressed data buffer when it is done with it, after writing to the file. (I know delete is deprecated but switching this to use an malloced buffer would have needed me to totally duplicate the std.zlib class so meh, we can do it if needed but for now let's just try this). Since my computer can't handle the large arrays anyway, I can't test this for sure but I expect you'll see some difference here. If you've changed TrueColorImage to use malloc/free as well that would probably help as well (none of this keeps a reference to data, so free it in truecolorimage's destructor and you should be ok.)
May 13 2013
On Friday, 10 May 2013 at 23:18:31 UTC, maarten van damme wrote:I'm trying to generate images and after displaying them I want to save them to a png file. I use parallelism to generate them asynchronously with the gui-thread to avoid blocking (don't know if this is really relevant). If I add a single line in the method where I generate the image that creates a class which allocates a pretty big array, slowly but surely the memory starts to rise until it ends in a spectacular out of memory exception. I always thought the garbage collector would deal with it. I tried reducing it but failed. Maybe I should let dustmite bite in it a bit later. How can I manually fix this? Is this a known bug in the garbage collector? btw, this code also caused segfaults on my 64 bit laptop on linux. How can I avoid this? main class: https://dl.dropboxusercontent.com/u/15024434/picturegenerator/generalised.d zip file with everything ready to go to compile: https://dl.dropboxusercontent.com/u/15024434/picturegenerator/picturegenerator.zip1) your code is not compilable on 64x system due to erroneous treating of some platform-varying types as uint, use size_t. Luckily dmd has -m32 switch 2) I encounter not out of memory exception but enforcement failure in simpledisplay.d:1024 "Maximum number of clients" which looks like not a memory error but you running out of some X server limit. 3) Segfault occures in simpledisplay.d:1058 - in destructor you call XDestroyImage(handle) but do not check whether handle is null. However it happens after enforcement failure so it doesn't looks like root of problem. 4) What really consumes memory is kernel or something which forces it to consume it since my task manager reports two blocks of 280 MB and 126 MB inside kernel part of virtual memory.
May 13 2013
I'll check every uint occurance, thank you. I'm on windows so I won't get the "maximum number of clients" error you talk about. The loop I used inside main was : "while(true){ curtrans=generateTransformationMatrix(); for(int y=0;y<height;y++) for(int x=0;x<width;x++) i.data[(y*width+x)*4..y*width+x)*4+4]=colorify(applyTransformation(transformXY(x,y),curtrans)).dup[0..3] ~ 255; // and finally write the data to a png file png = pngFromImage(i); //std.file.write("images/"~toHexString(md5Of(curtrans))~".png", writePng(png)); }" This doesn't use simpledisplay anymore so this should work fine on linux too? Adam checks this thread so he'll probably read the errors about simpledisplay. if not I'll copy paste and put in a bug report on github. -- The new fixes from adam rupe for png and image.d actually get rid of all memory leakages in my reduced version (without simpledisplay). Now I can get to finetuning my functions for the best images :) I'll see if the simpledisplay version now works too (leave it running overnight). -- So, do we actually know where the memory problems came from? is anyone actually running out of memory too or am I the only one? (tested on both my pc and my laptop so should be reproducible) 2013/5/13 Maxim Fomin <maxim maxim-fomin.ru>On Friday, 10 May 2013 at 23:18:31 UTC, maarten van damme wrote:I'm trying to generate images and after displaying them I want to save them to a png file. I use parallelism to generate them asynchronously with the gui-thread to avoid blocking (don't know if this is really relevant). If I add a single line in the method where I generate the image that creates a class which allocates a pretty big array, slowly but surely the memory starts to rise until it ends in a spectacular out of memory exception. I always thought the garbage collector would deal with it. I tried reducing it but failed. Maybe I should let dustmite bite in it a bit later. How can I manually fix this? Is this a known bug in the garbage collector? btw, this code also caused segfaults on my 64 bit laptop on linux. How can I avoid this? main class: https://dl.dropboxusercontent.**com/u/15024434/** picturegenerator/generalised.d<https://dl.dropboxusercontent.com/u/15024434/picturegenerator/generalised.d> zip file with everything ready to go to compile: https://dl.dropboxusercontent.**com/u/15024434/**picturegenerator/** picturegenerator.zip<https://dl.dropboxusercontent.com/u/15024434/picturegenerator/picturegenerator.zip>1) your code is not compilable on 64x system due to erroneous treating of some platform-varying types as uint, use size_t. Luckily dmd has -m32 switch 2) I encounter not out of memory exception but enforcement failure in simpledisplay.d:1024 "Maximum number of clients" which looks like not a memory error but you running out of some X server limit. 3) Segfault occures in simpledisplay.d:1058 - in destructor you call XDestroyImage(handle) but do not check whether handle is null. However it happens after enforcement failure so it doesn't looks like root of problem. 4) What really consumes memory is kernel or something which forces it to consume it since my task manager reports two blocks of 280 MB and 126 MB inside kernel part of virtual memory.
May 13 2013
On Monday, 13 May 2013 at 21:28:34 UTC, maarten van damme wrote:Adam checks this thread so he'll probably read the errors about simpledisplay. if not I'll copy paste and put in a bug report on github.I won't be on my linux box with free time until at least tomorrow afternoon but i'll check then. I've used simpledisplay on linux before, but never with a 64 bit app so i'll have to try it.So, do we actually know where the memory problems came from? is anyone actually running out of memory too or am I the only one?I didn't actually get that far since i'm on my slow computer, but my guess is one of those copies got pointed into. The image in your code is something like 8 MB of memory, and is generated pretty rapidly, as fast as teh cpu could do it in the other thread. The old function took that 8MB and made at least two copies of it, once in my function and once in std.zlib's function. If the usable address space is 2 GB, any random 32 bit number would have about a 1/128 chance of pointing into it. If you consider the gc was probably scanning at least one copy of that data for pointers - std.zlib's one was constructed as a void[], so even if allocator told the gc what type it was (idk if it does or not), void[] has to be scanned conservatively because it could be an array of pointers. And then add in random numbers on the stack, and the odds are pretty good the gc will get a false positive and then fail to free the memory block. Repeat as it loops through the images, and you've got a pretty big memory leak adding up. I'm not 100% sure that's what happened, but I think it is pretty likely. The reason the new function fixes it is because it manages the big copies manually, freeing them at the end of the function without worrying about false pointers.
May 13 2013
So now pgen.d works (without simpledisplay version an reusing trueimage). The simpledisplay version still has problems, but I'll try (manually (de)allocating)/(reusing) the data 2013/5/14 Adam D. Ruppe <destructionator gmail.com>On Monday, 13 May 2013 at 21:28:34 UTC, maarten van damme wrote:Adam checks this thread so he'll probably read the errors about simpledisplay. if not I'll copy paste and put in a bug report on github.I won't be on my linux box with free time until at least tomorrow afternoon but i'll check then. I've used simpledisplay on linux before, but never with a 64 bit app so i'll have to try it. So, do we actually know where the memory problems came from? is anyoneactually running out of memory too or am I the only one?I didn't actually get that far since i'm on my slow computer, but my guess is one of those copies got pointed into. The image in your code is something like 8 MB of memory, and is generated pretty rapidly, as fast as teh cpu could do it in the other thread. The old function took that 8MB and made at least two copies of it, once in my function and once in std.zlib's function. If the usable address space is 2 GB, any random 32 bit number would have about a 1/128 chance of pointing into it. If you consider the gc was probably scanning at least one copy of that data for pointers - std.zlib's one was constructed as a void[], so even if allocator told the gc what type it was (idk if it does or not), void[] has to be scanned conservatively because it could be an array of pointers. And then add in random numbers on the stack, and the odds are pretty good the gc will get a false positive and then fail to free the memory block. Repeat as it loops through the images, and you've got a pretty big memory leak adding up. I'm not 100% sure that's what happened, but I think it is pretty likely. The reason the new function fixes it is because it manages the big copies manually, freeing them at the end of the function without worrying about false pointers.
May 14 2013