1. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  2. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still havn't received your rank award? Then please contact the administration.
    Dismiss Notice
  3. The Lich King demands your service! We've reached the 19th edition of the Icon Contest. Come along and make some chilling servants for the one true king.
    Dismiss Notice
  4. The 4th SFX Contest has started. Be sure to participate and have a fun factor in it.
    Dismiss Notice
  5. The poll for the 21st Terraining Contest is LIVE. Be sure to check out the entries and vote for one.
    Dismiss Notice
  6. The results are out! Check them out.
    Dismiss Notice
  7. Don’t forget to sign up for the Hive Cup. There’s a 555 EUR prize pool. Sign up now!
    Dismiss Notice
  8. The Hive Workshop Cup contest results have been announced! See the maps that'll be featured in the Hive Workshop Cup tournament!
    Dismiss Notice
  9. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

Memory Leaks

Discussion in 'Trigger (GUI) Editor Tutorials' started by IcemanBo, Mar 6, 2015.

  1. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,180
    Resources:
    22
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    4
    JASS:
    3
    Resources:
    22
    Memory leaks


    For a quick lookup on most important actions, read Things That Leak.




    Introduction

    What is a memory leak?

    Memory leak is if your computer's memory is occupied with things you lost access to. It needs RAM and may slow down the PC for no benefits.
    Having tons of leaks in our Warcraft III maps for example could lead to lag-spikes during game sessions and in extreme cases upto a game freeze.

    Data Types

    If something can leak depends on its data type, so let's have a short look:


      • Primitives never leak.
      • Enums never leak.
      • Objects can leak!
    • There exist 6 primitive/native data types:
      integer , boolean , real , string , code , handle

      As GUI user you maybe didn't read something about
      code
      or
      handle
      so far, but don't worry, it's nothing required here to know.
    • You can imagine enums as special form of an integer but with a new name. (thanks Aniki)

      Enums

      Code (vJASS):
      race
      alliancetype
      racepreference
      gamestate igamestate fgamestate
      playerstate
      playerscore
      playergameresult
      unitstate
      aidifficulty
      eventid
      gameevent
      playerevent
      playerunitevent
      unitevent
      limitop
      widgetevent
      dialogevent
      unittype
      gamespeed
      gamedifficulty
      gametype
      mapflag
      mapvisibility
      mapsetting
      mapdensity
      mapcontrol
      fogstate
      playerslotstate
      volumegroup
      camerafield
      playercolor
      placement
      startlocprio
      raritycontrol
      blendmode
      texmapflags
      effecttype
      version
      itemtype
      attacktype
      damagetype
      weapontype
      soundtype
      pathingtype


    • Objects can be something like
      effect , unit , timer , group , multiboard
      .
      And much more! Basically everything that you can create or destroy is an object!



    Object Leaks

    What is an object leak?

    If an object exists, but you can't use it anymore it's considered an object leak.
    For example all in-game created objects should always be destroyed after they fulfilled their purpose, before you lose access to them.

    Example where objects are created:

    So an object is no primitive data type (see above), but it's something you can create, like a unit, or a unit group.

    • Unit - Create 1 Footman for Player 1 (Red) at (Center of (Playable map area)) facing Default building facing degrees
    "(Center of (Playable map area))" does create a new point object. As it is, it will leak.

    • Unit Group - Pick every unit in (Units in (Playable map area)) and do (Actions)
      • Loop - Actions
    "(Units in (Playable map area))" does create a new unit group object. As it is, it will leak.

    So the above lines use objects to operate. Always when objects are involved, there's a risk to leak.
    But be aware that not only actions might use objects, but also condtions can use them:

    • (Number of units in (Units in (Playable map area))) Equal to 0
    "(Units in (Playable map area))" does here create a new unit group object, too! As it is, it will leak, even it's in a condition.
    How to prevent object leaks?

    Some examples to destroy objects:

    • Destructible - Remove <Destructible> Object

    • Environment - Remove <Weathereffect>

    • Image - Destroy <Image>

    • Floating Text - Destroy <FloatingText>

    • Item - Remove <Item>

    • Leaderboard - Destroy <Leaderboard>

    • Lightning - Destroy <Lightning>

    • Multiboard - Destroy <Multiboard>

    • Unit - Remove <Unit> from the game


    Example

    At some point in the game we want to create a special effect. And to prevent a leak, we'll also call its destroy function afterwards. It looks like:
    • Special Effect - Create a special effect at ( ... ) using Abilities\Spells\Human\ThunderClap\ThunderClapCaster.mdl
    • Special Effect - Destroy (Last created special effect)
    GUI sadly does not provide all destroy functions we need.
    Sometimes we have to use a custom script in here. But don't worry it's always only one line.

    Locations

    • Set Location = (Center of (Playable map area))
    • ... Usage of Location ...
    • Custom script: call RemoveLocation(udg_Location)
    Unit Groups

    • Set Group = Pick every unit in (Playable Map Area)
    • ... Usage of Group ...
    • Custom script: call DestroyGroup(udg_Group)
    For groups you can even use an other method.
    If you set "bj_wantDestroyGroup" to "true" via custom script, it will automatically destroy the next unit group creation, that is done via GUI.

    • Custom script: set bj_wantDestroyGroup = true
    • Unit Group - Pick every unit in (Playable Map Area) and do (Unit - Hide (Picked unit))
    Player Groups

    • Set PlayerGroup = (All enemies of Player 1 (Red))
    • ... Usage of PlayerGroup ...
    • Custom script: call DestroyForce(udg_PlayerGroup)
    Be warned that "(All Players)" does not leak, as it is a constant variable. Never destroy it!
    Following does not leak, and can be used just like that:
    • Player Group - Pick every player in (All players) and do (Actions)
      • Loop - Actions

    There are some things which are worth an extra note:


      • Destroying something does still play its death animation.
      • Units get automatically removed after their corpse decayed.
      • Items are never being automatically removed.
    • If you for example destroy a special effect directly after its creation, the death animation will still correctly play:
      • Special Effect - Create a special effect at Location using Abilities\Spells\Human\ThunderClap\ThunderClapCaster.mdl
      • Special Effect - Destroy (Last created special effect)

      It's also applayable for items, units and destructibles. Killing/destroying will always play the death animation, while removing won't!
    • When a unit dies it still exists in the game, but it's just dead. Usually it lets some corpse behind and can be revived at some later point.
      Only after the corpse decayed, it means the unit was fully removed from the game. Then it's the same as you would use:
      • Unit - Remove Unit from the game
      So it's usually not required to clean up after normal unit deaths, as the game cleans them up on its own.
    • Items never get properly removed after they died, or were used (Tomes/Powerups).
      If you want dead items being automatically cleaned up you can take usage of a system which does it for you:



    Technique of removal

    We often use variables that help us to refer to the object being destroyed.
    Important is that you don't overwrite the variable with other values before the object is actually destroyed!

    Let's go by simple example where we want to destroy two locations:

    Good example: Bad example:
    • Set loc = Location_1
    • Unit - Move Unit_1 to loc
    • Custom script: call RemoveLocation(udg_loc)
    • Set loc = Location_2
    • Unit - Move Unit_2 to loc
    • Custom script: call RemoveLocation(udg_loc)

    • Set loc = Location_1
    • Unit - Move Unit_1 to loc
    • Set loc = Location_2 // OVERWRITTEN
    • Unit - Move Unit_2 to loc
    • Custom script: call RemoveLocation(udg_loc)
    In the bad example you lost access to Location_1, because the variable was overwritten.
    Location_1 can't be removed anymore, so it will leak.

    Reference Leaks

    << If you use GUI you can skip this chapter. >>​


    What is a reference leak?

    Firstly, only children types by the agent type are affected by the reference leak. Other handles don't suffer from it.
    An agent basically contains one address to an object, and one reference counter for it.
    • The object itself is what we have access to. It can be a unit, group, timer, or what ever. You have powers to destroy this object on yourself.
    • The reference counter is the number of variables pointing on this agent. It increases/decreases automatically when ever you set your variables in JASS.
    Imagine C struct of an agent
    Code (C):
    struct Agent
    {
       Object* object;
       int referenceCounter;
    };

    The agent struct you see above automatically allocates and also deallocates its memory, once it's not needed anymore.
    For us, it's only important to know that next to the object itself, there's a bit more memory in use for the agent.
    And there's actually a problem with this memory being automatically deallocated, even if we destroy the used object behind.
    To let this memory for the agent automatically deallocate, you have to destroy the object and have to ensure its reference counter is 0, meaning there are no more variables pointing on it.

    If the agent can not be deallocated anymore, due to an reference counter above 0, this is what is called reference leak. Its agent's id, the handleid, can never be recycled.​

    How to prevent reference leaks?

    We want to ensure an agent's reference counter equals 0, when we're about to destroy its object.
    To achieve this we always let our variables point on something else, after they fullfilled their purpose and before they fall out of scope.
    null
    comes handy here, as you may apply it to any
    agent
    type.

    Example:
    Code (vJASS):
    function foo takes nothing returns nothing
        local group g = CreateGroup()

        call DestroyGroup(g)
        set g = null
    endfunction
    At the top of the function we increase the reference counter for the agent on which
    g
    refers to, but we also ensure it's decreased again at the end of the function. And done! The agent won't have any issue to free its memory after the function has ended, because its reference counter is 0.​

    Technique of removal

    When the variable does point to
    null
    , it does of course also mean that it doesn't point to the agent anymore. So, if you want to destroy the object, you firstly must ensure to destroy the object before you
    null
    the variable.

    Good example: Bad Example:
    Code (vJASS):
    local group g = CreateGroup()
    call DestroyGroup(g)
    set g = null
    Code (vJASS):
    local group g = CreateGroup()
    set g = null
    call DestroyGroup(g)
    In the bad example the destroy function will fail, because
    g
    does not point to the group anymore. It would result in an object leak.​


    Miscellaneous


    Things that always leak

    There are some leaks that you can't prevent.
    • A unit will always keep a tiny bit of memory, even when removed properly. 0.04 kb and unpreventable. (Link)

    • Terrain deformations leak. Including default abilities like shockwave. (reference leak)

    • Very much BJs (Blizzard JASS functions) which are mostly used in GUI have reference leaks.
      You might always look them up to see how a BJ is defined at blizzard.j
    Other Tips
    • If the black screen freezes after game-end it may be an indicator for huge amount of leaks. Don't worry if it's a only a few seconds - it might also be the normal memory usage your map needs.

    • Lags are not necessarily a proof for leaks. Highly inefficient coding may also result in lags for example.

    • An indicator for major leaks might be noticed in task manager. Just watch the memory usage of WC3 while running the map.
      If it's increasing very fast and
      continuously, you might check your triggers again. (code that runs periodicly or is called very often is most important)

    • As GUI handles certain object initialization by default, (for arrays index [0] and [1]), it does technically leak once if you create for example a unit group for the first time and so overwrite the initialy created group, without having destroyed it. Example creation:
      • Set Group = (Units owned by Player 1(Red))
      ^But this leak fires only once, as you would destroy your self-created groups each time after usage. So this is good to know, but has no real effect.

    Conclusion
    • Always remove objects after usage, else you will have an object leak.
    • Always null agent type variables, else you will have a reference leak.
    • Object leak is the major leak, but the reference leak is still a leak so don't neglect it.
    That's it, thanks for reading! If you have anything to add or did not understand something, feel free to post. :csmile:
     
    Last edited: Sep 23, 2019
  2. LordDz

    LordDz

    Joined:
    May 11, 2007
    Messages:
    4,308
    Resources:
    0
    Resources:
    0
  3. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,180
    Resources:
    22
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    4
    JASS:
    3
    Resources:
    22
    "Things That Leak" is okay, but in my opinion the user could be teached some more general information about leaks.
    It is like an extension, but in tutorial section. There will be probably a link in "Things That Leak" that will link here.
    Also I have not really found anything about reference leaks on THW. These explainations might be new for most users.
     
  4. Quilnez

    Quilnez

    Joined:
    Oct 12, 2011
    Messages:
    3,251
    Resources:
    37
    Icons:
    2
    Tools:
    1
    Maps:
    7
    Spells:
    21
    Tutorials:
    2
    JASS:
    4
    Resources:
    37
    I have my own definition of leak, I think this is more general, including "memory leak" defined in other programming languages as well. I'm not an expert so don't trust me.

    Memory leak is where you create something (in this case agent) which is never been automatically destroyed by the system except if the program is ended (halt). Thus if you de-reference it without destroying, you will never be able access it anymore whereas it's still there. It consumes memory, but you have no access and can not remove it anymore except if the program (in this case, the map) is closed. So you can imagine how memory leaks make your map become heavier over times.

    Phew, I think wikipedia (Memory leak) define it better after all LOL. In short, memory leak is allocating memory space without deallocating that space later.

    Other information:
    Someone can indicate whether his/her map is badly leakful if the map needs some "freeze moment" when the game is closed, including restart, return to menu, or exit game. Perhaps you can add this.
     
    Last edited by a moderator: Jul 19, 2015
  5. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,426
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    It was decided through PM's that IcemanBo's guide would prove useful since it gives a deeper explanation of what is going on with leaks. In general, it is better to know the reason behind removing leaks than just knowing the functions to remove them.

    @IcemanBo: One small fix before it is ready for approval (didn't notice in my first skim):
    The handle ID leak only occurs with local variables. There is a minor leak associated with not nulling globals, but it is really insignificant (most likely just things associated with an objects reference count, which is static and probably takes up a few bytes at most). It is optional to remove in the case of globals. In locals, it is mandatory (at least for approval)--for all non-permanent agents.
     
  6. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,180
    Resources:
    22
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    4
    JASS:
    3
    Resources:
    22
    Okay, PurgeandFire (the wise) explained to me via PM why not nulling globals leaks a bit different as with locals.
    It is an other type of reference leak, which I did not know. But the point is, that it can be totaly ignored for realistic usage.
    Changed.
     
  7. Dat-C3

    Dat-C3

    Joined:
    Mar 15, 2012
    Messages:
    2,470
    Resources:
    10
    Models:
    1
    Maps:
    5
    Spells:
    3
    Tutorials:
    1
    Resources:
    10
    Doesn't creating new units/hero's in-game make permanent leaks that aren't cleanable? I know they do, but some likely won't believe me. Also you should make a note about shockwave as well war stomp. This is likely only for triggers/code, but technically its all code. Good tutorial. By the way you should always clean as many leaks as possible even when your map/game ends to prevent leaks from carrying over to your next game. Though what you can do to clear your memory is to restart WC3 entirely, close it then start it back up again.

    Edit: It's easy enough to test quickly if anyone needs to find out for themselves. Just clean all the leaks you know how too then continue to make units and remove them about 10,000 times for a noticeable number. Same with other stuff if you don't clean it, at least we get to clean some of the leaks I suppose. =)
     
  8. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,180
    Resources:
    22
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    4
    JASS:
    3
    Resources:
    22
    Yes, units will always use a bit of memory. Also added something from Dalvengyr's post.
    There might be some more things that always leak, I did not analyse each single function.
    There also might be some more interesting things to know that I could add to "Miscellaneous".
    I'm positive that we can gather most interesting stuff together with community's participation. :)
     
  9. edo494

    edo494

    Joined:
    Apr 16, 2012
    Messages:
    3,852
    Resources:
    5
    Spells:
    1
    JASS:
    4
    Resources:
    5
    since Jass is event driven, this is not true, because I can lose all references to a unit, but I can still get more, if the unit fires some event.

    Wiki definition is also not really that good, because all memory is deallocated when you end the program, or at worst, when you turn off computer, so technically you never create memory leaks by that definition.
     
  10. Zwiebelchen

    Zwiebelchen

    Joined:
    Sep 17, 2009
    Messages:
    6,791
    Resources:
    12
    Models:
    5
    Maps:
    1
    Spells:
    1
    Tutorials:
    1
    JASS:
    4
    Resources:
    12
    I think this tutorial is straight and to-the-point. The "Things that leak" sticky isn't as descriptive and tutorial-esque as this submission. I vote for approval.
     
  11. Flux

    Flux

    Joined:
    Feb 6, 2014
    Messages:
    2,334
    Resources:
    28
    Maps:
    1
    Spells:
    19
    Tutorials:
    2
    JASS:
    6
    Resources:
    28
    The in-depth details were useful. +rep
     
  12. BPower

    BPower

    Joined:
    Mar 18, 2012
    Messages:
    1,738
    Resources:
    21
    Spells:
    15
    Tutorials:
    1
    JASS:
    5
    Resources:
    21
    I think you should replace "a certain boolean = true"
    with "bj_wantDestroyGroup = true"

    It's a good tutorial
     
  13. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,180
    Resources:
    22
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    4
    JASS:
    3
    Resources:
    22
    ^Changed it. Also I came up with some more imporvements.
     
  14. blancfaye7

    blancfaye7

    Joined:
    May 21, 2014
    Messages:
    560
    Resources:
    1
    Maps:
    1
    Resources:
    1
    So even when a unit dies in-game (or should I say, finished decaying), it causes leaks? (I am not asking of how much it would leak, but an answer to that is appreciated as well.)

    BTW, best tutorial about leaks. Ever.
     
    Last edited: Apr 11, 2015
  15. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,180
    Resources:
    22
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    4
    JASS:
    3
    Resources:
    22
    Yes, units will permanently occupy a small part of memory, even after removed properply. (0.04kb iirc)
    Ty! :)
     
  16. blancfaye7

    blancfaye7

    Joined:
    May 21, 2014
    Messages:
    560
    Resources:
    1
    Maps:
    1
    Resources:
    1
    Oh my... That is indeed bad for my map :sad:
    Good thing I did not make any dummy units at all.
    But the spawns...

    Ah well... :( Thanks for the info!

    You should also add that this function:
    • Camera - Pan Camera as needed...
    also causes permanent leaks.
     
  17. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,180
    Resources:
    22
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    4
    JASS:
    3
    Resources:
    22
    Ah, right. This function anyway should not be used, and I will note it shortly, thanks.

    But well, 0.04 kb is not much. Don't think too much about it.
     
  18. danger_friendGR

    danger_friendGR

    Joined:
    Apr 30, 2013
    Messages:
    4
    Resources:
    0
    Resources:
    0
    Special

    Ye that's so helpful icemanbo is da best and he is SPECIAL!!!He knows better! :D
     
  19. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,426
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
  20. Flux

    Flux

    Joined:
    Feb 6, 2014
    Messages:
    2,334
    Resources:
    28
    Maps:
    1
    Spells:
    19
    Tutorials:
    2
    JASS:
    6
    Resources:
    28
    Since Unit groups are initialized at Map Initialization as Empty Group, that means the first time using
    • Set Group = (Units within Range of Location)

    will create a leak since ^that action overwrites the Empty group right?
    Code (vJASS):

    //Map Init
    set udg_Group = CreateGroup()

    //When using the GUI Pick Units the first time, the previously pointed unit group that was
    //created at Map Init leaks
    function GetUnitsInRangeOfLocMatching takes real radius, location whichLocation, boolexpr filter returns group
        local group g = CreateGroup()
        call GroupEnumUnitsInRangeOfLoc(g, whichLocation, radius, filter)
        call DestroyBoolExpr(filter)
        return g
    endfunction