HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Memory leak causes

10-15-2003, 09:43 PM#1
jmoritz
Maybe a list of known causes memory leaks would be helpful? Here's what I found so far:

Removing units (doh)
PauseUnit
returning location (more?) handles through jass functions
Not destroying objects when they should (doh) --> GUI

This means that some functions in common.j and Blizzard.j leak by simply using them (PolarProjectionBJ for example). Everybody should avoid using these functions.
10-15-2003, 10:50 PM#2
dataangel
What evidence is there that returning a function causes a memory leak? If that were true any map that used recursion would experience leaks -- I use it extensively in Battledome with locations and I don't have any leaks.
10-17-2003, 06:50 PM#3
jmoritz
A function (code) is not a handle. I've only tested this with location so far, but my guess is every handle leaks when returned. Also note that passing handles as parameters to other functions does not leak; I'm only talking about returning.
10-17-2003, 08:18 PM#4
AIAndy
I can't really see why handles would cause memory leaks when returned. They are just integers where the value of the integer specifies a place in an internal array of Warcraft.
You can actually convert the handles into integers using a bug in the return type recognition.
And since they are integers, why would returning them cause any leaks? There is no actual copy of the content made.
10-18-2003, 03:34 AM#5
dataangel
Quote:
Originally posted by jmoritz
A function (code) is not a handle. I've only tested this with location so far, but my guess is every handle leaks when returned. Also note that passing handles as parameters to other functions does not leak; I'm only talking about returning.


How did you test it? My map returns a function that returns a location a few hundred times everytime I play my map with even a few people, and I haven't noticed any leaks.
10-22-2003, 12:05 PM#6
jmoritz
The setup:
A trigger that fires when the player types "test". The action function contains a loop (1 to 10,000), and a local variable called "temploc". Boot war3 (Ctrl+F9 in WE). Open task manager with Ctrl+Shift+Escape and look at mem usage of war3.exe process. Then run loop once and check again. Then run loop 20 times and check again.

Inside this loop I tested the following:
Code:
function testlocals takes nothing returns location
  local location testloc = null
  set testloc = Location(10, 20)
  return testloc
endfunction

Loop
  set temploc = testlocals()
  call RemoveLocation(temploc)
endloop
This is why I first said returning locations leaked. After running the loop 20 times, mem usage goes up by roughly 12 megs.
Code:
function testlocals takes nothing returns location
  return Location(10,20)
endfunction

Loop
  set temploc = testlocals()
  call RemoveLocation(temploc)
endloop
This doesn't leak at all, except for initial leak (discussed later below).
Code:
function testlocals takes nothing returns location
  local location testloc = null
  set testloc = null
  return testloc
endfunction

Loop
  set temploc = testlocals()
endloop
Doesn't leak at all, no initial leak either.
Code:
function testlocals takes nothing returns location
  local location testloc = null
  set testloc = Location(10, 20)
  call RemoveLocation(temploc)
endfunction

Loop
  call testlocals()
endloop
This leaks. Note this is the same code as the first test, except I moved the RemoveLocation to the testlocals function.
Code:
Loop
  set testloc = Location(10, 20)
  call RemoveLocation(testloc)
  set testloc = Location(10, 20)
  call RemoveLocation(testloc)
  set testloc = Location(10, 20)
  call RemoveLocation(testloc)
  set testloc = Location(10, 20)
  call RemoveLocation(testloc)
endloop
Why 4 times? To make certain I was not mistaken :)
This leaks only the first time it is run. Roughly 12 megs. The second time you run it, it doesn't leak anymore.

Conclusions for now (more testing is needed to be certain):
When no object is created, war3 doesn't leak. RemoveLocation doesn't clean up the entire object. However, in some cases the "left-overs" are recycled.

The code for Interceptor movement in PR caused a nice leak because of this...
10-22-2003, 01:37 PM#7
Grater
I used this function, run by trigger once every second.
Code:
    local integer i=0
    local location l
    loop
    exitwhen i > 10000
    set l = mytest()
    call RemoveLocation(l)
    set i =i +1
set udg_count = udg_count +1
    endloop

As you can see, I verify the number of times it has run. I had a timer recording elapsed time.

Heres the results:
It leaked approximately 600K each minute, during which time it ran about 518,000 times.

The problem is thus: It's "leaking" about 1 byte each time the "mytest" function runs... If it was leaking handles, they should surely be 4 bytes and thus have a leak rate of something like 2meg a minute.

But that still doesn't explain where the memory has actually gone... after all a "control" map with no units and only simple triggers doesn't leak 500K over a minute.

I have a hunch it's something to do with the fact that the "testing" triggers arent running to completion (count was not a multiple of 10,000), so the triggers themselves are building up in the trigger queue and thus consuming memory. Unfortunately I dont know much at all about the trigger queue etc...
10-22-2003, 01:43 PM#8
AIAndy
It might be that the place in the array (or whatever it is) that is used to store all the handles is not recycled in the leaking cases.
Try to convert the location into an integer and look how high that integer is at the last run of the loop.

For converting use this:

function Handle2Int takes handle h returns integer
if h != null then
return h
endif
return 0
endfunction
10-22-2003, 05:43 PM#9
Peppar
I tried testing these functions. Notice the difference.

This one leaks:

Code:
function CreateLocation takes nothing returns location
    local location a = Location(0, 0)
    call RemoveLocation(a)
    return a
endfunction

This one doesn't leak:

Code:
function CreateLocation takes nothing returns location
    local location a = Location(0, 0)
    call RemoveLocation(a)
    set a = null
    return a
endfunction

My theory is that among other factors, Warcraft III avoids to assign a handle a new value if it suspects that there still might be a reference to that handle somewhere in the script. This way, the handle array gets huge.

1.9 million locations were created over several minutes using the first function, and the memory usage increased by 100 mb over the same period. The second method didn't have a noticable increase, so I didn't test it for as long.
10-22-2003, 06:06 PM#10
Cacodemon
Another bug (but it's not causes memory leak) appears when adding special effect on dying unit just after death. Bug appears effect has unit stand animation model! After 5-10 minutes bug disappears.
10-22-2003, 10:52 PM#11
Grater
Quote:
1.9 million locations were created over several minutes using the first function, and the memory usage increased by 100 mb over the same period. The second method didn't have a noticable increase, so I didn't test it for as long.
If those numbers are accurate then thats over 50 bytes per time the function runs, seems a bit unlikely to me.

The setting to null thing sounds interesting tho.
10-22-2003, 11:07 PM#12
Cacodemon
You can try this:

function CreateLocation takes nothing returns location
return Location(0, 0)
endfunction

2 Peppar: I'm sorry, but second function returns location with coordinates (0,0), not null? I can't believe my eyes :) Is it really truth?
10-22-2003, 11:15 PM#13
Grater
Quote:
My theory is that among other factors, Warcraft III avoids to assign a handle a new value if it suspects that there still might be a reference to that handle somewhere in the script. This way, the handle array gets huge.
I read that a few more times and it makes a lot of sense, I know I often use a check for NULL to determine if I can recycle an array index, it makes sense the blizzard programmers would do something simialler.
10-23-2003, 01:08 PM#14
jmoritz
If a script calls RemoveLocation, it means the programmer of the script tells the engine to destroy the location object. There is no reason to not destroy it. Also, RemoveLocation does remove the object, so any handle that might still point to the object is now invalid anyway.

The set a = null sounds very interesting. If that works, I might have a solution for the memory leaks in my map :)

The fact that the functions do not run to completion might have something to do with the memory leaks, if there is garbage collection going on at the end of a function, that doesn't happen when the function ends prematurely. That doesn't explain why some leaks only happen the first time you run the function though.
10-23-2003, 01:22 PM#15
Cacodemon
Code:
function CreateLocation takes nothing returns location
    local location a = Location(0, 0)
    call RemoveLocation(a)
    set a = null
    return a
endfunction

.. Or I just don't understand you? This function returns null anyway. So what is it for?