HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

When do I need to set a handle to null to prevent leaks?

05-05-2005, 03:46 PM#1
Tommi
When do I need to set a handle or any of its subtypes to null to prevent memory leaks?
Do I need to do it after every RemoveLocation() and Destroy...() call?
05-05-2005, 04:07 PM#2
Vexorian
You should do at before a function ends and only for local variables ( arguments for some reason don't need this)

It doesn't matter if you don't destroy the handle it seems.

And of course if you want to destroy the handle you have to do that before setting the variable to null
05-05-2005, 04:19 PM#3
Tommi
Ok, so basically I check all handle-type/subtype local variables in every function & trigger and set their values to null at the end of the function (or before any return statement), and I'm ok?
05-05-2005, 09:40 PM#4
weaaddar
Yes, but not doing it isn't such a big deal.
What do I mean?
Let say you create a unit and do some modulation to it inside the function
Code:
function Foobar takes nothing returns unit
local unit u=CreateUnit(...)
SetUnitSomething(...)
return u
endfunction
This isn't such a big deal of a leak. Cubasis and other suggests cheat though and do this
Code:
function Foobar takes unit u returns unit
set u=CreateUnit(...)
SetUnitHp(...)
return u
endfunction

whereby the unit is a bullshit pointer. Ofcourse if you really give this much of a damn...well then I suggest this which doesn't require the b.s. pointer but does require H2I.
Code:
function Foobar takes nothing returns unit
local unit u=CreateUnit(...)
local integer i=H2I(u)
SetUnitSomething(...)
set u=null
return i
return null
endfunction
Simple and no b.s. pointer required.
05-05-2005, 10:50 PM#5
Anitarf
So, you eliminate the leak from the local unit by seting it to null, ok, I get that, it's standard procedure (and with unit groups and points, you must destroy them before seting the variable to null)... and the leak from the local integer is eliminated by returning null after you return the integer? That's because you can't set things to null after the return statement, if I remember correctly from another thread? So, any object (unit, unit group, location...) can be returned mem-leak free using either the b.s. pointer, or by converting it's handle to integer and returning that, and after that returning null?

Sorry if I got it all wrong, but I'm still learning about how JASS (and object oriented programming in general) works, and I figure if I'm ever going to need it, it will be for some massive systems (like calculating backstab on every attack, that sort of stuff) that will definitely need to be memory leak free.
05-05-2005, 11:18 PM#6
weaaddar
Local Atomic types (int,real,boolean) are cleared of memory alocated at the end of a function.
Local Handle Types (everything else except string and code) are only cleared of memory alocated if they are not pointing to anything.

This means, an integer does not leak, but a unit can. The leak is very small, because it is a pointer leak.

This statement:
local unit u=CreateUnit(...)
Does three things. First it creates a local pointer. It creates an index on the handle stack (this is the value you get when you H2I(u) ), and it creates the unit it self.

Now if you remove the unit, your problems are solved right?
Not quite. The Index won't be recycled if there are any pointers to it. However, jass is also retarded and decided that pointers, local or otherwise, won't be deleted if they are pointing to a handle index and not 0 (null)
So in actuality, the pointer and the index still exist at the end of the function, even if the unit is removed in it.

Now why isn't this such a big deal? First of handle indexs are never deleted. They are always at best recycled. Let say you have 10 player deatchmatch with no new units spawning. Once the units are vanquished the handle indexs will not be reset, even if they have no pointers pointing to them.

Also most of the time, your function is running on a protagonist type unit, a main character. So that unit isn't being removed at all. So keeping his index "alive", by one extra local pointer shouldn't matter as you probably already have a pointer for him.

Another thing to add to the stop worrying tab, is that a local pointer is thought to be around 4-16 bytes (four being how much is should actually be, and around 16 gotten by an estimate by AIAndy). Lets assume the worst and say it is 16. You would need to leak 62,500 pointers for even one meg of added ram abuse.

Now, of course if it is perserving an unused handle index that can start adding up though. A handle index is around 40-50kb in size. Even then you'd need about 200 to start caring.

Don't take this as a meaning to leak like a whore though. This is just one leak that isn't that big of a deal. Still get rid of locations and groups.

EDIT:
The return null is part of a technique an exploit in the jass checker. It doesn't check if all returns types are the one they are supposed to be, only if the last one is.
05-06-2005, 12:09 AM#7
Anitarf
Wait, is this the infamous "return bug" that I've heard mentioned before in relation to H2I? Basically you return an integer, but hide that from the syntax checker so it doesn't know that and doesn't cause problems with functions that require a unit (or some other handle type) parameter?

And, to recap, local atomic types don't leak a thing and may be used to my heart's desire? However, in order to allow a handle index to be recycled, I must destroy that handle's object (with DestroyGroup, RemoveLocation and such) and also all local pointers that point to it (by setting them to null). But it's different with global pointers, somehow?... because in mem leak tutorials it doesn't state anywhere you would have to set a global variable to null after you destroy the handle object it was pointing to (such as a unit group) to prevent the memory leak.

Edit: ahh, never mind, it makes sense to me now that things are the same for all pointers, but globals are globals, you will most certainly re-use them eventually to point to something else and at that point the handle previously pointed to will be "free", that's why it's only the locals we need to worry about.
05-06-2005, 12:27 AM#8
weaaddar
H2I, is the quintessential return bug function.

Basically, you got the idea with the globals. A global will be reassigned, so you don't have to worry about it maintaining a handle index. You ideally should set it to null right after destroying it, but if you don't you'll probably reassign it so the handle index will be considered free when a new object is creating.
05-06-2005, 11:57 AM#9
Tommi
Ok, the third case: arguments.
Lord Vexorian said that you don't need to set handle type arguments to null after destroying their contents. Do you agree?
05-06-2005, 12:12 PM#10
Tommi
Ok, I want some confirmation for these:

Code:
function DuplicateLoc takes location loc returns location
    return Location( GetLocationX(loc) , GetLocationY(loc) )
endfunction
DuplicateLoc doesn't leak, because there are no local variables, albeit I'm creating a new object, right?


Code:
//Used to get the target location in an area when the area is changed to another without a triggering unit (e.g. through the area menu)
function GetAreaTargetLoc takes integer pIndex, integer targetregion returns location
    local group g = null
    local location loc = null
    local integer i = 0

    //Select a hero from the region
    set g = GetHeroesInRectOfPlayer(udg_Area[targetregion], Player(pIndex-1))
    if ( IsUnitGroupEmptyBJ( g ) == true ) then
        //call DisplayTimedTextToForce( GetPlayersAll(), 5.00, "Target Region Index (GetAreaTargetLoc): "+ I2S(targetregion) )
        if targetregion == udg_DumAturRegion then
        //if Dum Atur Yard
            set loc = DuplicateLoc(udg_DefaultPlayerPoint[pIndex])
           //call DisplayTimedTextToForce( GetPlayersAll(), 5.00, "Dum Atur Default Point" )
        else
            set loc = GetRectCenter(udg_Area[targetregion])
           //call DisplayTimedTextToForce( GetPlayersAll(), 5.00, "Rect Center" )
        endif
    else
        //select the first hero
        set loc = GetUnitLoc(FirstOfGroup( g ))
        //call DisplayTimedTextToForce( GetPlayersAll(), 5.00, "Hero Unit Loc" )
    endif

    call DestroyGroup( g )
    set g = null

    set i = H2I(loc)
    set loc = null

    return i
    return null
endfunction
This has been now fixed by the use of H2I, right? I tested this version in-game, and it worked, so at least it didn't break the function. ;)
05-06-2005, 02:50 PM#11
Vexorian
Weeaddar is alive?

Anyways, not setting it to null is not big deal unless the function is used a lot of times in really consecutive processes, That's why this leak is specially bad on blizzard.j's multiboard item functions.

DuplicateLoc won't leak at all unless you don't Remove its returned location once you no longer need it
05-06-2005, 04:24 PM#12
weaaddar
Don't let the secret out.
But yeah, its not terribly big leak so thats why its okay. Its also better if its referencing handle indexs which won't be dying anytime.
e.g.
function Objects takes nothing returns gamecache
if (udg_gc==null)then
set udg_gc=InitGameCache("blah.w3v")
endif
return udg_gc
endfunction

function foo takes integer s returns integer
local gamecache gc=Objects()
local integer i=GetStoredInteger(gc,"foo","bar")
call StoreInteger(gc,"foo","bar", s )
return i
endfunction

the gamecache handle leak isn't significant at all, as that handle index is almost certainly to be still in use for a long while after.
05-06-2005, 04:29 PM#13
Jackyquah
if handle is leaks what about all function with handle parameter, isn't that mean it's leak too ?
05-07-2005, 12:10 AM#14
Vexorian
Quote:
Originally Posted by Jackyquah
if handle is leaks what about all function with handle parameter, isn't that mean it's leak too ?
It is weird, but the leak of the handle local variables seems to be more of a bug, as you noted arguments should leak too, but they don't.
05-07-2005, 01:23 PM#15
Jackyquah
How do you detect the leak ?

Is Blizzard confirmed that there is a leak(bugs) in local handle ?