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,701
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
509
Why isn't this in Tutorial Submission?

tutorialsubmissionua8.jpg


:rolleyes:

It's kind of misleading...
 
General chit-chat
Help Users
  • No one is chatting at the moment.
  • Ghan Ghan:
    Still lurking
    +3
  • The Helper The Helper:
    I am great and it is fantastic to see you my friend!
    +1
  • The Helper The Helper:
    If you are new to the site please check out the Recipe and Food Forum https://www.thehelper.net/forums/recipes-and-food.220/
  • Monovertex Monovertex:
    How come you're so into recipes lately? Never saw this much interest in this topic in the old days of TH.net
  • Monovertex Monovertex:
    Hmm, how do I change my signature?
  • tom_mai78101 tom_mai78101:
    Signatures can be edit in your account profile. As for the old stuffs, I'm thinking it's because Blizzard is now under Microsoft, and because of Microsoft Xbox going the way it is, it's dreadful.
  • The Helper The Helper:
    I am not big on the recipes I am just promoting them - I use the site as a practice place promoting stuff
    +2
  • Monovertex Monovertex:
    @tom_mai78101 I must be blind. If I go on my profile I don't see any area to edit the signature; If I go to account details (settings) I don't see any signature area either.
  • The Helper The Helper:
    You can get there if you click the bell icon (alerts) and choose preferences from the bottom, signature will be in the menu on the left there https://www.thehelper.net/account/preferences
  • The Helper The Helper:
    I think I need to split the Sci/Tech news forum into 2 one for Science and one for Tech but I am hating all the moving of posts I would have to do
  • The Helper The Helper:
    What is up Old Mountain Shadow?
  • The Helper The Helper:
    Happy Thursday!
    +1
  • Varine Varine:
    Crazy how much 3d printing has come in the last few years. Sad that it's not as easily modifiable though
  • Varine Varine:
    I bought an Ender 3 during the pandemic and tinkered with it all the time. Just bought a Sovol, not as easy. I'm trying to make it use a different nozzle because I have a fuck ton of Volcanos, and they use what is basically a modified volcano that is just a smidge longer, and almost every part on this thing needs to be redone to make it work
  • Varine Varine:
    Luckily I have a 3d printer for that, I guess. But it's ridiculous. The regular volcanos are 21mm, these Sovol versions are about 23.5mm
  • Varine Varine:
    So, 2.5mm longer. But the thing that measures the bed is about 1.5mm above the nozzle, so if I swap it with a volcano then I'm 1mm behind it. So cool, new bracket to swap that, but THEN the fan shroud to direct air at the part is ALSO going to be .5mm to low, and so I need to redo that, but by doing that it is a little bit off where it should be blowing and it's throwing it at the heating block instead of the part, and fuck man
  • Varine Varine:
    I didn't realize they designed this entire thing to NOT be modded. I would have just got a fucking Bambu if I knew that, the whole point was I could fuck with this. And no one else makes shit for Sovol so I have to go through them, and they have... interesting pricing models. So I have a new extruder altogether that I'm taking apart and going to just design a whole new one to use my nozzles. Dumb design.
  • Varine Varine:
    Can't just buy a new heatblock, you need to get a whole hotend - so block, heater cartridge, thermistor, heatbreak, and nozzle. And they put this fucking paste in there so I can't take the thermistor or cartridge out with any ease, that's 30 dollars. Or you can get the whole extrudor with the direct driver AND that heatblock for like 50, but you still can't get any of it to come apart
  • Varine Varine:
    Partsbuilt has individual parts I found but they're expensive. I think I can get bits swapped around and make this work with generic shit though
  • Ghan Ghan:
    Heard Houston got hit pretty bad by storms last night. Hope all is well with TH.
  • The Helper The Helper:
    Power back on finally - all is good here no damage
    +2
  • V-SNES V-SNES:
    Happy Friday!
    +1
  • The Helper The Helper:
    New recipe is another summer dessert Berry and Peach Cheesecake - https://www.thehelper.net/threads/recipe-berry-and-peach-cheesecake.194169/

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top