GC memory leak
BlitzMax Forums/BlitzMax Programming/GC memory leak
| ||
Calling the empty method whatever() causes a memory leak:Type thing Global list:TList=New TList Method New() list.addfirst Self EndMethod Method whatever() EndMethod EndType GCCollect() b=GCMemAlloced() a:thing=New thing 'uncomment this and the mem leak occurs' a.whatever() thing.list.remove a a=Null GCCollect() Notify b+", "+GCMemAlloced() End |
| ||
bah, i hope this isn't the case for types where the sub type has a non-empty method of the same name |
| ||
The problem is more or less the TList While it has a clear in its destructor method, this one does never seem to be called. If you use TLists, make sure that you manually clear them before you "null-ify" them at the current state. |
| ||
@Dreamora: please can you explain with an example what do you mean? I've a similar problem and I want to resolve it in the 'right' way thanks |
| ||
so list.clear is not acceptable? |
| ||
list.clear is acceptable or even needed I should say. degac: The problem is that the TList default destructor (method delete() ) has clear in it. Normally this would mean that all content is cleared when the TList goes out of scope. But something in its structuring (the links are applied as cyclic group which is bad on a GC system without secure root link checking, which BMs GC seems to be) fails badly. So if you null-ify something with a linked list or a linked list itself, you have to manually clear the list. As short example: strict type TContainer field list:TList = createlist() method destroy() list.clear() list = null end method method add(obj:Object) list.addlast(obj) end method end type gccollect() ' Mem Clear to clear the initialisation garbage local cont:TContainer = new TContainer local a:int = GCMemAlloced() cont.add("Test 1") cont.add("Test 2") 'cont.destroy() '<---- Uncomment this line and see what happens cont = null gccollect() print "memory difference: " gcmemalloced() - a I should note that doing GCMemAlloced stuff on such a low memory usage level is quite pointless normally. It only works on BMs currently as it isn't written speed optimized but more toward memory optimized. A normal GC won't free any memory from system RAM unless its needed as dealloc takes time that could be used for more important stuff. So if you want to do "memory leak tests" you normally have to take care that you use several MBs at least and see what happens then, as GC behavior is "unpredictable" on no ram usage (Where 1 reference is having a weight in the output as the example above of me or leadwerks) |
| ||
So, to clarify: if you use a lot of Lists, and you need to "reset" them between Map or Level loads, .clear() is the way you should be doing it? |
| ||
I'm not getting any memory leak with your example. Niether with a.whatever commented or uncommented. I ran the code in a loop to see if anything accumulates after a while, nothing even then. |
| ||
For resetting them, clear is the way anyway instead of recreating a new one. But you as well need to call clear if you either do list = null or with an existing list list = new TList / createlist() ie when the list is going to go out of scope and be garbage collected. |
| ||
Everything "Dreamora" said about this is gibberish. "List" is a global parameter, not a field. It doesn't get destroyed. The difference is calling the method whatever() causes an increased memory usage after the object is freed. If the method is not called, memory usage stays the same. I created this simple example after I isolated the issue in my own code. It is causing my program to increase 1 mb of memory usage per second. |
| ||
I don't see any memory leak with a.whatever() uncommented in that code either. |
| ||
Run in release vs Debug |
| ||
Is it really a 'leak'? If you run it in a loop it grabs that first bit of memory but doesn't continue to grab it... Type thing Global list:TList=New TList Method New() list.addfirst Self EndMethod Method whatever() EndMethod EndType For x = 0 To 10 GCCollect() b=GCMemAlloced() a:thing=New thing 'uncomment this and the mem leak occurs' a.whatever() thing.list.remove a a=Null GCCollect() Notify b+", "+GCMemAlloced() Next End |
| ||
Release version 'seems' to lose just a few bytes - it's constant too, no matter how many times the creation/destruction is called. The debug version shows no memory leak at all. |
| ||
Leadwerks: I mentioned that this is with list in general and I as well mentioned that your "show off" as mine are quite pointless as a GC does not work memory critical when you have a memory usage that is that low that +- 1 reference makes a difference. There is no leak in TList usage, just in TList collecting if you haven't manually cleared it. (btw: leaks can't be shown by a 1 time operation but by something thats done through looping and raises at least constantly with the amount of steps which is not the case here) |
| ||
I still don't see a constant loss of bytes. Debug : 15250 15250 for each loop. Rel : 14434 14442 first loop then 14442 14442 constantly. |
| ||
Hi, tonyg's da man... *Always* look for mem leaks in loops! In fact, I think this may be in the docs...? Looking for leaks in straight line code like this is silly - there are 1001 things that can keep something 'live' longer than you think it should be - debug/release mode, register allocation/spillage etc etc. However, if you stick code in a loop and memory usage doesn't eventually 'settle', there's a problem. And Dreamora, please note that the list is global! I don't really get any of the points you have made in this thread, but perhaps you've overlooked this fact. Whatever, your comments are very confusing! |
| ||
Thank you for the feedback. I was looking for the cause of a memory leak, and was attempting to reduce the problem. I eventually identified a place where I had accidentally set an integer variable equal to a type. Changing the variable from an integer to the type fixed the leak. |
| ||
right, well glad that's cleared up then... |
| ||
Dreamora, and for anybody else who's worried, run that code in a loop...Strict Type TContainer Field list:TList = CreateList() Method destroy() list.clear() list = Null End Method Method add(obj:Object) list.addlast(obj) End Method End Type For Local x:Int = 0 To 10 GCCollect() ' Mem Clear to clear the initialisation garbage Local cont:TContainer = New TContainer Local a:Int = GCMemAlloced() cont.add("Test 1") cont.add("Test 2") 'cont.destroy() '<---- Uncomment this line and see what happens cont = Null GCCollect() Print "memory difference: " + (GCMemAlloced() - a) Print GCMemAlloced() Next |