Tutorial Memory Leaks In JASS

Andrewgosu

The Silent Pandaren Helper
Reaction score
716
Memory Leaks In JASS
There are plenty of memory leak tutorial around. Some of them are brilliant, others are average, but mainly, they are for GUI users. Experienced JASSers mainly know how to deal with memory leaks in JASS, but a lot of beginners tend to forgot them. This tutorial is designed to help the poor, lost souls in the vast lands of memory leaks in JASS.

There is no difference between a memory leak in JASS and a memory leak in GUI. A memory leak remains a memory leak. However, in order to clean a memory leak, one firstly has to recognize it. I’ll show most of the things, that leak, in an "question&answer" form, so, if You JASS, You can easily avoid code which leaks.

Note
, please ignore the "pseudo-variables", which I haven’t declared. These functions are examples and the leaking part counts.

Leaks, which will be covered:

- Nulling local variables
- Location
- Group
- Force
- Special effect
- Lightning
- Trigger actions
- Timer


So, shall we begin?

- Nulling local variables

Before we get jiggly with removing and destroying, many beginners forget to do the simplest thing to avoid memory leaks – they forgot to null local handle variables. And what is a local handle variable? Handle variable is a variable that is not a string, real, integer, code or boolean type variable. Easy? Lets take an example.

JASS:
function NullifyLeakage takes nothing returns nothing
    local unit a = GetTriggerUnit()
    local real x = GetUnitX(a)
    local real y = GetUnitY(a)   
    //Some actions.
    set a = null
endfunction


Now, if one forgets to nullify the unit variable, it will result in a leak – it will be floating in the memory for the rest of the game. So, to say over, all local variables except string, real, integer, code and boolean have to be nulled.

Note, handle variables have to be nulled after they have been destroyed removed. One cannot destroy a variable, which is null.

- Location leakage

JASS:
function LocationLeakage takes nothing returns nothing
    call SetUnitPositionLoc( GetSpellTargetUnit(), GetUnitLoc(GetTriggerUnit()))
endfunction


The location leaks. To clean it up, one has to set it into a variable.

JASS:
native RemoveLocation takes location whichLocation returns nothing


JASS:
function LocationLeakage takes nothing returns nothing
    local location loc = GetUnitLoc(GetTriggerUnit())
    call SetUnitPositionLoc( GetSpellTargetUnit(), loc )
    call RemoveLocation(loc)

    set loc = null
endfunction


However, one doesn't need to use locations at all - locations consist of two reals - the x- and y-axis values. (Alot of native functions take 2 reals instead of location and using reals is faster, too!)

JASS:
function RealUsage takes nothing returns nothing
    local unit a    = GetTriggerUnit()
    local real xPos = GetUnitX(a)
    local real yPos = GetUnitY(a)
    call SetUnitPosition( GetSpellTargetUnit(), xPos, yPos )

    set a = null
endfunction


- Group leakage

JASS:
function GroupLeakage takes nothing returns nothing
    call ForGroup( GetUnitsOfPlayerAll(Player(0)), function whichFunction )
endfunction


This function leaks a group. To fix it, set it into a variable.

JASS:
native DestroyGroup takes group whichGroup returns nothing


JASS:
function GroupLeakage takes nothing returns nothing
    local group g = GetUnitsOfPlayerAll(Player(0))
    call ForGroup( g, function whichFunction ) 
    call DestroyGroup(g)
    
    set g = null
endfunction


Or, use the "set bj_wantDestroyGroup" action, which destroys the group after it has been used. But setting the group into a variable is wiser, if one needs the same group several times.

JASS:
function GroupLeakage takes nothing returns nothing
    set bj_wantDestroyGroup = true
    call ForGroup( GetUnitsOfPlayerAll(Player(0)), function whichFunction )
endfunction


- Force leakage


JASS:
function ForceLeakage takes nothing returns nothing
    call DisplayTimedTextToForce( GetForceOfPlayer(GetOwningPlayer(GetTriggerUnit())), 5., "Hey, I create a force every time and leak" )
endfunction


This leaks, because it creates a new force every time. To fix it, do,

JASS:
native DestroyForce takes force whichForce returns nothing


JASS:
function ForceLeakage takes nothing returns nothing
    local force f = GetForceOfPlayer(GetOwningPlayer(GetTriggerUnit()))
    call DisplayTimedTextToForce( f, 5., "Hey, I don't leak" )
    call DestroyForce(f)
    
    set f = null
endfunction


- Special effect leakage


JASS:
function EffectAndPointLeakage takes nothing returns nothing
    call AddSpecialEffectLoc( modelName, GetUnitLoc(GetTriggerUnit()) )
endfunction


This leaks a location and an effect, if one forgets to remove them after usage. A common mistake when making spells.
JASS:
native DestroyEffect takes effect whichEffect returns nothing


JASS:
function EffectAndPointLeakage takes nothing returns nothing
    local location loc = GetUnitLoc(GetTriggerUnit())
    local effect   e   = AddSpecialEffectLoc( modelName, loc )
    //Some actions.
    call DestroyEffect(e)
    call RemoveLocation(loc)
    
    set loc = null
    set e   = null
endfunction


This takes care of the point and effect leak, however, if one needs the effect to be destroyed right after it has been created, the code can be shortened(Lets use a native, which takes 2 reals, too).

JASS:
function EffectLeakage takes nothing returns nothing
    call DestroyEffect(AddSpecialEffect( modelName, x, y ))
endfunction


Note
, effects created on units have to be removed as well.

JASS:
function EffectLeakage takes nothing returns nothing
    local effect e = AddSpecialEffectTarget( modelName, GetTriggerUnit(), "overhead" )
    //Some actions.
    call DestroyEffect(e)

    set e = null
endfunction


- Lightning leakage


The same deal is with lighting as it was with effects - they have to be destroyed.

JASS:
function LightningLeakage takes nothing returns nothing
    call AddLightning( codeName, checkVisibility, x1, x2, y1, y2 )
endfunction


If not removed, it will cause a leak.

JASS:
native DestroyLightning takes lightning whichBolt returns boolean


JASS:
function LightningLeakage takes nothing returns nothing
    local lightning light = AddLightning( codeName, checkVisibility, x1, x2, y1, y2 )
    //Some actions.
    call DestroyLightning(light)

    set light = null
endfunction


- Trigger action leakage

Trigger action leaks are rare, mainly, when triggers are created via triggers. Removing trigger actions is quite tricky, one has to store the trigger action to access it later so it wouldn't leak.

Here is a modified piece of Kattanas Handle Variable system, which allows to store and access trigger actions(A global game cache variable named "Cache" is needed. Copy paste the following script into your maps header"). If You already have the system, just make sure You have the "GetHandleTriggerAction" function.

JASS:
function H2I takes handle h returns integer
    return h
    return 0
endfunction

function LocalVars takes nothing returns gamecache
    if ( udg_Cache == null ) then
        call FlushGameCache(InitGameCache("somename"))
        set udg_Cache = InitGameCache("somename")
    endif
    return udg_Cache
endfunction

function SetHandleHandle takes handle subject, string name, handle value returns nothing
    if ( value == null ) then
        call FlushStoredInteger(LocalVars(),I2S(H2I(subject)),name)
    else
        call StoreInteger(LocalVars(), I2S(H2I(subject)), name, H2I(value))
    endif
endfunction

function GetHandleTriggerAction takes handle subject, string name returns triggeraction
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function FlushHandleLocals takes handle subject returns nothing
    call FlushStoredMission(LocalVars(), I2S(H2I(subject)) )
endfunction


Now, we are prepared to clean trigger actions leaks.

JASS:
function ExampleAction takes nothing returns nothing
    local unit a = GetTriggerUnit()
    if ( IsUnitType( a, UNIT_TYPE_HERO ) == true ) then
        call ReviveHero( a, x, y, doEyeCandy )
        call DestroyTrigger(GetTriggeringTrigger())
    endif

    set a = null    
endfunction

function TriggerActionLeakage takes nothing returns nothing
    local trigger       t  = CreateTrigger()
    call TriggerAddAction( t, function ExampleAction )
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_DEATH )
    
    set t = null
endfunction


Now, this destroys the trigger, but not the triggeraction. This causes a leak. To fix it, we have to attach the triggeraction to the created trigger and destroy it before we destroy the trigger.

JASS:
native TriggerRemoveAction takes trigger whichTrigger, triggeraction whichAction returns nothing


JASS:
function ExampleAction takes nothing returns nothing
    local unit    a = GetTriggerUnit()
    local trigger t = GetTriggeringTrigger()
    
    if ( IsUnitType( a, UNIT_TYPE_HERO ) == true ) then
        call ReviveHero( a, x, y, doEyeCandy )
        //This removes the triggeraction.
        call TriggerRemoveAction( t, GetHandleTriggerAction( t, "ta" ) ))
        //This removes the triggeraction from the gamecache.
        call FlushHandleLocals(t)
        call DestroyTrigger(t)
    endif

    set a = null
    set t = null
endfunction

function TriggerActionLeakage takes nothing returns nothing
    local trigger       t  = CreateTrigger()
    local triggeraction ta = TriggerAddAction( t, function Example )
    
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_DEATH )
    //This attaches the triggeraction to the created trigger.
    call SetHandleHandle( t, "ta" ta )

    set t = null
    set ta = null
endfunction


- Timer leakage

JASS:
function ExampleTimer takes nothing returns nothing
    local integer nIndex = 0
    local player  p
    loop
        exitwhen ( nIndex == bj_MAX_PLAYERS )
        set p = Player(nIndex)
        call SetPlayerState( p, PLAYER_STATE_RESOURCE_GOLD, GetPlayerState( p, PLAYER_STATE_RESOURCE_GOLD ) + 100 )
        set nIndex = nIndex + 1
    endloop
    
    set p = null
endfunction

function TimerLeakage takes nothing returns nothing
    local timer t = CreateTimer()
    call TimerStart( t, 30, false, function ExampleTimer )
    
    set t = null
endfunction


This piece of code leaks a timer, because it is never removed. Destroy the timer to fix the leak.

JASS:
native DestroyTimer takes timer whichTimer returns nothing


JASS:
function ExampleTimer takes nothing returns nothing
    local integer nIndex = 0
    local player  p
    loop
        exitwhen ( nIndex == bj_MAX_PLAYERS )
        set p = Player(nIndex)
        call SetPlayerState( p, PLAYER_STATE_RESOURCE_GOLD, GetPlayerState( p, PLAYER_STATE_RESOURCE_GOLD ) + 100 )
        set nIndex = nIndex + 1
    endloop         
    call DestroyTimer(GetExpiredTimer())
    
    set p = null
endfunction

function TimerLeakage takes nothing returns nothing
    local timer t = CreateTimer()
    call TimerStart( t, 30, false, function ExampleTimer )
    
    set t = null
endfunction
 

DuckieKing

Elitist Through and Through
Reaction score
51
I like this tutorial. It's clear, it has good use of the bertiful new JASS tags (were you waiting for them?)... All in all a good tutorial. +rep
I'd remark on how miniscule the leak is if you don't null a local. If you leak 5000 of them it should be barely noticeable on old machines.
all local variables expect string, real, integer, code and boolean have to be nulled.
First to fix a typo! ^_~
 

Andrewgosu

The Silent Pandaren Helper
Reaction score
716
I'd remark on how miniscule the leak is if you don't null a local. If you leak 5000 of them it should be barely noticeable on old machines.

But we want to be safe, just in case, don't we? :) Thanks for the feedback. Fixed the typo.
 

Ninja_sheep

Heavy is credit to team!
Reaction score
64
Nice tutorial, learned somthing. (+rep)

But isn't it a bit stupid to use BJs in a tutorial? so i might come on the idea to use them ;)
 

Arkan

Nobody rides for free
Reaction score
92
I believe this could be very helpful for people learning JASS, nice!

You might wanna add that you should pause a timer (if it is periodic) before destroying it, heard there's a nasty bug that timers might be going on forever otherwise.
 

Andrewgosu

The Silent Pandaren Helper
Reaction score
716
But isn't it a bit stupid to use BJs in a tutorial? so i might come on the idea to use them ;)

I didn't want to make it confusing for people, because, they are just examples.

You might wanna add that you should pause a timer (if it is periodic) before destroying it, heard there's a nasty bug that timers might be going on forever otherwise.

Never heard about that bug. It's sounds a little doubtable, a timer which runs after it has been destroyed...
 

SFilip

Gone but not forgotten
Reaction score
634
Not all local handles need to be nulled, you don't need to null those that will never be destroyed until the end of the game (such as players or heroes unless you use RemoveUnit on them). However it might be hard to think about these things sometimes so I guess it's better to just null them all.
Also there is a much easier way to account for trigger actions. Check out this topic.
According to what I heard timers shouldn't be nulled if you're using a H2I based system (such as Kattana's). They might mess so that you lose anything you "attached". Never confirmed whether this is true, but better safe than sorry. It's actually the best to do what AceHart said and recycle - create a couple of timers at map init, store them somewhere and pause/re-start when needed.
 

The Helper

Necromancy Power over 9000
Staff member
Reaction score
1,703
I moved this back to Tutorial Submission. Any reason why it should not be here?
 

Doom-Angel

Jass User (Just started using NewGen)
Reaction score
167
Great Tutorial Andrew (+Rep :p)

gave me some knowlesge about a few stuff :D
 

elmstfreddie

The Finglonger
Reaction score
203
"Handle variable is a variable, that is not a string, real, integer, code or boolean typed"
Bolded word should read "type", or preferably "variable". Just makes more sense. Also, no comma after second variable.
And oh yeah, a lot is two words.

Otherwise, good tutorial. I'm just learning JASS now :)eek: ), so it's best I learn about leaks early :)
 
Reaction score
456
Nice tutorial, except I didn't learn anything new.. Oh yeah, I didn't know about triggeraction leak :p
 

Cohadar

master of fugue
Reaction score
209
Code:
Nulling local variables

Ok this is NOT a leak

I would give you -rep for this but don't know how.

You are actually telling people to do the unnecessary thing here
thus adding to the overall JASS confusion

Explanation:

Code:
JASS locals are [B]scrambled[/B] after triger call has ended
they do NOT stay in memory pointing to objects and making leaks


Also:
Code:
I have heard something from Vexorian, than boolean expression don't leak, but it's up to You whether You clean them.

The reason bool expresions don't leak is the fact that they are actually
local hidden/temporary boolean variables witch also get scrambled after trigger end.

Detecting if something is a leak is not black magic

all you have to do is
1. make a dummy map
2. put a suspicious code in a trigger
3. make trigger calls say every 0.04 sec
4. let the map run for 5 min

when you start map , alt-tab
ctrl-alt-del -> windows task manager -> write down war3.exe memory usage (aproximatelly 60.000 K on freshly started map)

alt-tab to get back in game
(this is important war3 pauses game if you alt-tab in single player)
wait 10 min in game (use you mobile clock ffs)
(play some music in winamp before you start map to prevent boredom)

alt-tab again after 10 min and check war3.exe memory

if it gets like 100.000K and above, it is definitelly a leak
 

MaaxeEvid

New Member
Reaction score
8
congratulations great tutorial, but i really think that local are not leak, but now i know it is, thx
 

PurgeandFire

zxcvmkgdfg
Reaction score
508
Why isn't this in Tutorial Submission?

tutorialsubmissionua8.jpg


:rolleyes:

It's kind of misleading...
 
General chit-chat
Help Users

      The Helper Discord

      Staff online

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top