My favourite addon leaks memory
So I have realised that my favourite addon (which I wrote) is misbehaving. Wow, slowly gets worse and worse, until it slows to a crawl. A /reload fixes it until a little while later it does it again.
So I looked at the ? (Game Menu) and it shows the addon steadily increasing its memory while in combat. It starts at a modest 650K, but after some time it goes to many MBs. I presume this is related to the general issue I am facing. Before I start hacking the code apart, have they recently made any changes that might have caused this? |
We could rattle off all the changes they made. And that may or may not click something in your brain as to the problem. But that assumes that this issue wasn't present before the patch, which may or may not be true. A better solution would be to post your code and allow the problem to be solved objectively rather than through guesses.
|
OK, ignore all this.
a lua file has ... ... ... local BA_Data1 = { }; ... ... ... On some event local function someevent() BA_Data1["AAAA1"] = {}; end That leaks - I am not sure why... |
That's just Lua. When you create a new table and assign it to that key, the previous table, despite being empty, still exists. It's just unobtainable, floating in memory, until it gets released by garbage collection. This has been a known issue for years. Blizzard relatively recently developed "table pools" where they reuse tables, even empty tables.
Instead of assigning a new empty table to an object that already has a table, use wipe() instead. If your desired effect is to just make sure the object is a table, use type() before assigning it a table. |
What Kanegasi said. And you can't always rely on garbage collection for things like strings either, especially during events that trigger much like OnUpdate. Though, garbage collection is slightly more forgiving for strings... if only a little. Addons should have an initialization phase for every table where it's created and at any point you need to reset/reuse, or wipe, it you should use table.wipe.
I'm not sure how they're using the table but a reference/key can be read either as a string or a variable. Lua Code:
Lua Code:
|
I was 99.9% sure it was bad GC, and I wrote a big message yesterday, but then I re-wrote the whole message to make it simpler and forgot to mention the GC in the new edit.
OK so after some experimentation, I recap: 1) My addon leaks memory like a sieve. I presume when it has leaked enough it slows down WoW because it works on screen update, ie very often, and probably with large memory lying about some operations become slower 2) This manifests itself that we go into the raid, all is well, but by the 5th wipe on Rastakhan my screen slows to a crawl, effective 2-3 FPS 3) I discovered that a /reload fixes the issue, magically 4) You told me that my tables are all leaking, every time I do " local myTable = { } ". You suggested to use wipe(). 5) I tried to use wipe() but it failed because it does not help with tables inside tables. It also makes the code cumbersome and weird to read. 6) but we can force GC with " collectgarbage(); " - this seems to work beautifully. Now I am trying to find the best place to put this. From my experience 100 years ago, I remember that you don't want to be calling GC because it blocks all threads and can halt your code for a while. Actually I know of a very, very famous stock exchange, that decided to re-write their code in .. .net, and they had disabled GC for this reason. The exchange needed to be rebooted every evening to reclaim memory. So, back to WoW, I am slighly worried that if I call GC, it might affect ALL addons, and it might block all addons and threads, just when I am trying to taunt Bwonsamdi or cast Guardian Angel to the tank... Currently I have added GC during (a) GCD (b) casting (c) not in combat, hoping it should not affect things too much. |
Quote:
Concatenation, while it seems innocuous, is actually the biggest issue with memory bloat when it comes to strings. With each concatenation operation, you add a copy of said string with whatever piece you appended to it. Example: Code:
print("some".." ".."sample".." ".."text") Another big issue with strings is formatting them with numbers. Each time you format a string with numbers, each unique combination of numbers produces a new string object. |
Quote:
|
Why don't you just post your /real/ code? ;)
|
Quote:
Lua Code:
Quote:
|
Quote:
Quote:
|
I thought Blizzard indicated that tables did get picked up by GC when the coordinates functions(s) changed to return a table instead of x, y. Could also be I misunderstood.
|
Quote:
|
I wasn't saying garbage collection shouldn't be minimised just making sure I had my understanding about what it does/doesn't collect is correct. I presume, as jeruku said, frames are still not "collected".
|
Frames are a special occasion. The table portion has a reference stored in C code, so that doesn't get released to the GC. Not to mention the C side resources that aren't even exposed to Lua.
|
Many thanks for the confirmation.
|
OK I have traced deeper into the problem and have better news (for me).
To recap: WoW was getting progressively slow, to the point of crawling to a halt. It took a couple of hours to reach that point, two hours of trying Rastakhan on Heroic to be precise. I discovered that a /reload fixed it (after we wiped for the nth time). I then started to find the culprit, which was my own addon. I immediately noticed that my addon started at about 700K but during combat it would rise with no apparent let up. This could mean a lazy GC or something more sinister. After reading your suggestions I realised that any kind of table assignment increases the reported memory (hover mouse over the ?). wipe() did not fix it, nor did using local tables (you'd think local variables get thrown away at the end of the function if no references to them). I then discovered we can force GC with collectgarbage() and I used it liberally, and my memory consumption went down, however it made the screen jerky, so I removed it. Finally I found the culprit as being not memory or lazy garbage collection, but my misuse of "CreateFrame", as in gameTooltip = CreateFrame("GameTooltip", "BA_GameTooltip", UIParent, "GameTooltipTemplate"); which I used to parse the tooltip of Azerite gear in order to find the available traits. 3-4 times a second. This is what causes the screen jerkiness and eventually the slowdown to a crawl. I read on the wiki/gamepedia that you must re-use frames, they cannot be deleted! So I did just this, and all seems to be good. Memory still seems to be going up, but it was not the reason for the jerkiness and slowdown, it was all those frames I was creating. |
This is why you should post your code in the first place
|
I got a bit intrigued by the table wipe discussion since I used it for my first AddOn that I made (or rather an early version of my first AddOn). Since I had trouble with the tables keeping their old values when the UPDATE_BINDINGS event was triggered I had to wipe the tables as shown below.
Lua Code:
Would this actually be an OK way to do it, or would I get problems if the addon had been updating a lot more? |
Quote:
Quote:
|
All times are GMT -6. The time now is 08:53 AM. |
vBulletin © 2024, Jelsoft Enterprises Ltd
© 2004 - 2022 MMOUI