HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

LEAK CLEANING questions

04-30-2006, 08:42 PM#1
grupoapunte
Hi well i readed the leak fixing tutorials and im trying to know if i undestand the problem.

- Frist of all only the "handle" related variables leak.
- Second, cleaning leaks of temporal objects like UnitGroup it easy and those are tthe most common leaks (points also).
- Third and this is the worst one GUI functions like TriggeringUnit() leak (in jass GetTriggerUnit()).

When i first strated to make maps i realy didnt even know about JASS or memory leaks until i realized that for some reason ppl was droping from my map. So after that i started to learn how to fix GUI leaks by storing points in vars or groups, but now and for the third reason i listed above i realize that fixing those leaks was like fixing 1% of the real problem, because in GUI the use of things like TriggeringUnit(), ItemBeingManipulated() generate leaks for not NULLING variables.

This is a sample of one acction in my map:

Trigger:
Set GroupTeleport[(Player number of (Owner of (Triggering unit)))] = (Units within 1000.00 of (Position of (Triggering unit)) matching (((Matching unit) is A Hero) Equal to True))

Well yes im fixing the leak of the group by storing it (i release it later by the usage of DestroyGroup()) but in fact the function GetTriggerUnit() is leaking so im still messing up.

If i have this simple function:

Collapse JASS:
function func takes nothing returns nothing 
    call DisplayTextToForce( GetForceOfPlayer(GetOwningPlayer(GetManipulatingUnit())), "This function leaks a lot") 
endfunction

that will leak right so to fix it i would have to do this:

Collapse JASS:
function func takes nothing returns nothing 
    local unit u = GetManipulatingUnit() 
    local force f = GetForceOfPlayer(GetOwningPlayer(u)) 
    call DisplayTextToForce( f, "This function isnt leaking") 
    call DestroyForce(f) 
    set f = null 
    set u = null 
endfunction 

i suppose thats fine, but im wondering if GetOwningPlayer(u) leaks, and in case it does how do i clear it?

Am i missing something in the fixed function?
I dont have to clean the objects of the functions like GetTriggerUnit() right? (i imagina that doing it would end in removing the unit from the game) but i do have to nulify the value right? so i will nead to declare locals per every function of that kind that i need to use.

My final question is, whats a boolexpr? for example can i call the folowing code a boolexpr?

Collapse JASS:
If (i > 10)

I know that, that code returns a bool, which dont need to be nulify (types that dont need to be nulifyed: string, real, integer, code, boolean) but in the case that the "i > 10" were a boolexpr do i need to store it in a local var to be able to "DestroyBoolExpr(someBoolExpr)" later and that way aboid it from leaking?

In my opinion if all the things that i listed are leaks i just cant undestand how blizzard did such thing, i mean im a programmer (C, C++, VB.NET, Java, and others) this kind of bugs make me thingk twise about learning JASS because it makes imposible for the programmer to make a leak clean code, even in C++ i dont have to clean up so many things this is terrible, i wonder why WC3 dont has a Garbage Collector. No offence to al the ppl that realy like this code, in fact it makes many things posible and thats the reason why i want to learn it.

Thanks a lot, i hope i can learn to be a good JASS programmer.

Please correct me if im wrong in something.

PS: sry if i posted this in the wrong place, i tryed to post it under the leak tutorial section but no one answered.
04-30-2006, 08:54 PM#2
Dalten
in your first example: Position of (Triggering unit)

That leaks a location. You have to store the location in a variable, reference the variable in your Set GroupTeleport function and then RemoveLocation the point.
04-30-2006, 08:58 PM#3
Captain Griffen
Player groups, points and unit groups leak as variables. Special effects, dummy units, and regions leak if not destroyed, but the variables don't. Strings also leak, but that is unstoppable and negligable.

Globals do not need to be nullified. Nullifying stuff has no impact on leaks in GUI, only on locals.

Quote:
- Third and this is the worst one GUI functions like TriggeringUnit() leak (in jass GetTriggerUnit()).

Doesn't leak.

Collapse JASS:
function func takes nothing returns nothing 
    local unit u = GetManipulatingUnit() 
    local force f = GetForceOfPlayer(GetOwningPlayer(u)) 
    call DisplayTextToForce( f, "This function isnt leaking") 
    call DestroyForce(f) 
    set f = null 
    set u = null 
endfunction

No point in setting GenManipulatinUnit() to a variable.

I don't know where you got the idea that native functions as basic as GetTriggerUnit() would leak, but JASS isn't that bad.
04-30-2006, 09:02 PM#4
Thunder_Eye
for the "If (i > 10)" question.

youll have to put "then" after it to work. "If (i > 10) then"
I'm also not sure if it matters if "if" is "If" or "if". Use "if" to be sure.
04-30-2006, 09:16 PM#5
grupoapunte
well in fact the info about the natives leaking i got them from the tutorial in here and in WC3JASS.COM
04-30-2006, 09:21 PM#6
grupoapunte
theres the other tutorial, one of the functions that return a unit for example is: GetManipulatingUnit()

where unit is child of handle so it leaks and u must set it to null, and thats a native function.

http://www.wc3jass.com/viewtopic.php?t=2072

Edited by Blade.dk. Reason: Fixed tags.
04-30-2006, 09:26 PM#7
Captain Griffen
Quote:
Globals do not need to be nullified. Nullifying stuff has no impact on leaks in GUI, only on locals.

All locals have to be nullified, but as GUI doesn't use locals, GUI won't leak if you don't nullify them. It says that in the article:

Quote:
When you have used a local variable which type is handle

Oh, and don't double post. Edit instead.
05-02-2006, 09:02 PM#8
grupoapunte
I been looking at the blizzard.j file that has functions that as it says at the top, are needed in every map script, well i have a sample function here:

Collapse JASS:
function ReplaceUnitBJ takes unit whichUnit, integer newUnitId, integer unitStateMethod returns unit
    local unit    oldUnit = whichUnit
    local unit    newUnit
    local boolean wasHidden
    local integer index
    local item    indexItem
    local real    oldRatio

    // If we have bogus data, don't attempt the replace.
    if (oldUnit == null) then
        set bj_lastReplacedUnit = oldUnit
        return oldUnit
    endif

    // Hide the original unit.
    set wasHidden = IsUnitHidden(oldUnit)
    call ShowUnit(oldUnit, false)

    // Create the replacement unit.
    if (newUnitId == 'ugol') then
        set newUnit = CreateBlightedGoldmine(GetOwningPlayer(oldUnit), GetUnitX(oldUnit), GetUnitY(oldUnit), GetUnitFacing(oldUnit))
    else
        set newUnit = CreateUnit(GetOwningPlayer(oldUnit), newUnitId, GetUnitX(oldUnit), GetUnitY(oldUnit), GetUnitFacing(oldUnit))
    endif

    // Set the unit's life and mana according to the requested method.
    if (unitStateMethod == bj_UNIT_STATE_METHOD_RELATIVE) then
        // Set the replacement's current/max life ratio to that of the old unit.
        // If both units have mana, do the same for mana.
        if (GetUnitState(oldUnit, UNIT_STATE_MAX_LIFE) > 0) then
            set oldRatio = GetUnitState(oldUnit, UNIT_STATE_LIFE) / GetUnitState(oldUnit, UNIT_STATE_MAX_LIFE)
            call SetUnitState(newUnit, UNIT_STATE_LIFE, oldRatio * GetUnitState(newUnit, UNIT_STATE_MAX_LIFE))
        endif

        if (GetUnitState(oldUnit, UNIT_STATE_MAX_MANA) > 0) and (GetUnitState(newUnit, UNIT_STATE_MAX_MANA) > 0) then
            set oldRatio = GetUnitState(oldUnit, UNIT_STATE_MANA) / GetUnitState(oldUnit, UNIT_STATE_MAX_MANA)
            call SetUnitState(newUnit, UNIT_STATE_MANA, oldRatio * GetUnitState(newUnit, UNIT_STATE_MAX_MANA))
        endif
    elseif (unitStateMethod == bj_UNIT_STATE_METHOD_ABSOLUTE) then
        // Set the replacement's current life to that of the old unit.
        // If the new unit has mana, do the same for mana.
        call SetUnitState(newUnit, UNIT_STATE_LIFE, GetUnitState(oldUnit, UNIT_STATE_LIFE))
        if (GetUnitState(newUnit, UNIT_STATE_MAX_MANA) > 0) then
            call SetUnitState(newUnit, UNIT_STATE_MANA, GetUnitState(oldUnit, UNIT_STATE_MANA))
        endif
    elseif (unitStateMethod == bj_UNIT_STATE_METHOD_DEFAULTS) then
        // The newly created unit should already have default life and mana.
    elseif (unitStateMethod == bj_UNIT_STATE_METHOD_MAXIMUM) then
        // Use max life and mana.
        call SetUnitState(newUnit, UNIT_STATE_LIFE, GetUnitState(newUnit, UNIT_STATE_MAX_LIFE))
        call SetUnitState(newUnit, UNIT_STATE_MANA, GetUnitState(newUnit, UNIT_STATE_MAX_MANA))
    else
        // Unrecognized unit state method - ignore the request.
    endif

    // Mirror properties of the old unit onto the new unit.
    //call PauseUnit(newUnit, IsUnitPaused(oldUnit))
    call SetResourceAmount(newUnit, GetResourceAmount(oldUnit))

    // If both the old and new units are heroes, handle their hero info.
    if (IsUnitType(oldUnit, UNIT_TYPE_HERO) and IsUnitType(newUnit, UNIT_TYPE_HERO)) then
        call SetHeroXP(newUnit, GetHeroXP(oldUnit), false)

        set index = 0
        loop
            set indexItem = UnitItemInSlot(oldUnit, index)
            if (indexItem != null) then
                call UnitRemoveItem(oldUnit, indexItem)
                call UnitAddItem(newUnit, indexItem)
            endif

            set index = index + 1
            exitwhen index >= bj_MAX_INVENTORY
        endloop
    endif

    // Remove or kill the original unit.  It is sometimes unsafe to remove
    // hidden units, so kill the original unit if it was previously hidden.
    if wasHidden then
        call KillUnit(oldUnit)
        call RemoveUnit(oldUnit)
    else
        call RemoveUnit(oldUnit)
    endif

    set bj_lastReplacedUnit = newUnit
    return newUnit
endfunction

As you can see in this function variables like oldUnit and indexItem are never set to null, not to mention newUnit which can't be set to null because its the return value and as far as i know a function can't take any actions after returning (like in C) so i would like to know why ppl says that not nullying variables (locals, which are suposed to be dealocated at the end of a function) can cause a memory leak. In the case it does well all the functions in blizzard.j that uses locals that are handle or child of handle LEAK because i didnt see even one where the locals are set to null.

I undestand the reason why an object needs to be destroyed (deallocated) but theres no reason for nulling a pointer that is a local in a function, because the local is supposed to be auto-dealocated after the funcion ends.

Please someone experienced answer this question.

Thanks!

EDIT:

I realy dont find it logic, and it makes making clean maps imposible, because blizzard.j functions are in every map
05-02-2006, 09:41 PM#9
PipeDream
Yes, many functions in Blizzard.j will take bits of memory hostage. This is one reason we try to avoid Blizzard.j, however, I believe Vexorian's map optimizer fixes these leaks automagically, if your map is compatible with the appropriate option.
Quote:
I undestand the reason why an object needs to be destroyed (deallocated) but theres no reason for nulling a pointer that is a local in a function, because the local is supposed to be auto-dealocated after the funcion ends.
Yep, there's no reason for it. Stupid blizzard bug.
There are ways to work around the bug when you need to return the variable.
http://www.wc3campaigns.net/showthread.php?t=81872
05-02-2006, 09:43 PM#10
Vexorian
The game does have a garbage collector, it always cleans stuff once the map ends or you exit the game.

It is not that hard to avoid leaks, with some it is impossible(in JASS) but the ones that are not manually cleanable don't matter than much.

And it is not difficult to beat leaks in C++ either
05-02-2006, 09:57 PM#11
grupoapunte
Thanks ppl, ill try not to use blizzard.j functions then, anyways as im new to JASS programming and in the first place i wanted to learn it cuz my map has a lot of gui triggers and i wanted to give players with power less comps a chance to reach to the end of the game without droping.

So in fact what im doing is converting my gui triggers to jass and using locals to fix leaks, but then i found that most of the functions that i use come with leaks which is realy frustrating cuz i dont have time to make them from zero, so ill just do what i can, thanks for the help ppl.

EDIT:

you mentioned that vex optimizer can fix blizzard.j bugs if i select the right option, can you tell me whats the option plz?

Im actualy using the optimizer and i use both map optimization options, and script optimization methods checked

By he way, excelent tool man!
05-04-2006, 03:51 AM#12
The_AwaKening
There's an option that says Anti BJ.

Optimizer won't fix your all your leaks though. You'll have to continue converting your stuff to JASS and fix it manually. Agreed though, it is an awesome tool.
05-04-2006, 03:57 AM#13
Vexorian
It will only fix the blizzard.j leaks caused by variables not being set to null. The rest is for you to fix
05-05-2006, 06:56 PM#14
grupoapunte
what about the locals that come with the function declaration, i mean the parameters for example if i have:

Collapse JASS:
function Example takes player p returns nothing
    //do some stuff
endfunction

does the local var "p" leaks?

this depends on which the paramter is given to the function by reference or by value.

EDIT:

can destroy Start trigger after its execution? in case i can, can it be at the end of the trigger:

Collapse JASS:
function Init takes nothing returns nothing
    //do some stuff
   call DestroyTrigger(gg_trg_Init)
endfunction
05-05-2006, 09:42 PM#15
PipeDream
The only way to get a variable to take a handle hostage is with the set statement. Using a function argument, blessedly, will not incur leaks.

However, player variables will never result in a leak since you never deallocate them. You don't lose a little chunk each time you forget to change the variable before the function returns, you lose a little chunk if you once forgot to change when you deallocate the object.

You can destroy any trigger. Not everything is run by triggers. The engine calls the main function when the script begins. If you tried to destroy GetTriggeringTrigger there.. Probably nothing would happen.

Maybe it is run by a trigger, and you can reset the map by running it.

Edit: Oh, all arguments are passed by value. But I don't see how that's relevant. BTW, the only variable type is 4 byte long.