Memory Leaks

Level 12
Joined
Nov 7, 2014
Messages
566
It seems that structs/objects that bundle up primitive/other structs/objects types are referred to as "composite/aggregate" types and not "complex" (oh I guess it's a synonym).

Anyway... I have this list of lists that tries to "classify" Jass's types (it's fairly biased):

JASS:
primitive: [
    integer,
    real,
    boolean,
    string,
    code,
    handle,
],

intresting: [
    string, // any string when displayed can be colored using a code: |cffFF0000<this-text-will-be-displayed-in-red>|r
    unit, item, ability, buff,
    destructable,
    trigger, event,
    timer, timerdialog,
    rect,
    sound,
    camerasetup,
    effect,
    weathereffect,
    terraindeformation,
    fogmodifier,
    dialog, button,
    multiboard, multiboarditem, leaderboard,
    trackable,
    gamecache,
    texttag,
    lightning,
    image,
    <text-to-player>, // DisplayTimedTextToPlayer
    <cine-filter>, <cinematic-cine>,
],

unintresting: [
    player, // glorified integer
    agent,
    widget,
    force,
    group,
    triggercondition, triggeraction,
    quest, questitem, defeatcondition,
    location,
    region,
    boolexpr, conditionfunc, filterfunc,
    unitpool, itempool,
    ubersplat,
    hashtable,
],

not-savable-in-hashtable: [
    // code, // can't have code arrays (modulo bug) either so...
    camerasetup,
    weathereffect,
    terraindeformation,
    gamecache,
],

enums: [
    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,
],

@TODO: which of the handle extending types (non-enum) use handle-ids >= 0x00_10_00_00 and which use their own specific handle-id pool (e.g: texttag(s))

The reason I am pasting it is because of that TODO at the end which is related to handle-id leaks. So maybe we (by we I mean you ;P) can make two list of handles, the ones that have an id (GetHandleId) >= 0x00_10_00_00 and the ones that have ids starting from 1 I guess?

PS: I think its worth noting that every type can be convert and/or casted to/from an integer, some are just a bit harder than others. [1]
 
Last edited:
Level 12
Joined
Nov 7, 2014
Messages
566
@Aniki how you exactly detinguish between intresting and uninteresting?
Like I said, the list is biased, but lets see:
JASS:
uninteresting: [
    // I don't see the point of this type not being an integer or implicitly convertable to one.
    // Its just annothing having to call GetPlayerId (player -> integer) and Player (integer -> player)
    // manually all the time. I think in SC2 this type is indeed an integer.
    player,

    // I don't know why this type was introduced when Blizzard made the hashtable natives.
    // It seems pretty pointless.
    agent,

    // Units, items and destructables are widgets... so we have inheritance there... I just don't like
    // inheritance (I do like tagged/discriminated unions =), instead), but at least its only 1 level
    // of inheritance (I guess Blizzard learned their lesson from SC1 :P).
    // So okay, maybe not that uninteresting.
    widget,

    // A non-random-access collection of player(s) with callback iteration...
    force,

    // A non-random-access collection of unit(s) with optional callback iteration,
    // slightly better than force, thanks to FirstOfGroup, but FirstOfGroup has
    // issues of its own (GroupRefresh), so yeah... not really interesting.
    group,

    // Removing an event listener (trigger)'s handler functions...
    triggercondition, triggeraction,

    // These are here because they are cumbersome to read when one wants to put
    // a lot of text in the quests menu.
    quest, questitem, defeatcondition,

    // I wish location was a primitive type (like integer/real), i.e not having to bother
    // calling RemoveLocation and nulling them, but they are not, which makes them annoying to use,
    // unfortunately.
    location,

    // Just because of the confusion it creates with using the event "unit enters rect".
    region,

    // Code pointing to a function with a boolean return value?
    boolexpr, conditionfunc, filterfunc,

    // I don't think Blizzard used these in WE's jass generation, I guess they are kind of
    // intersting from algorithmic point of view.
    unitpool, itempool,

    // Image, ubersplat, what's the difference?
    ubersplat,

    // I think we have to thank dota's ubiquitous use of gamecache (and not structs, I don't think "dota knows" what a struct is)
    // and the return bug for this overused type. It's occasionally useful, but not as interesting as gamecahe.
    hashtable,
],
 
Thanks for your thoughts. Hm.. about the list of important obejcts, that have to be actually cared about (destroyed), without all the integer wrappers enums... I think that this might be a paragraph in "Data Types" topic.
But I also don't wanna bloat the tutorial, I try to find something short next days to mention it. (and just add a [HlDDEN] list or so)

The other seperation, might be interesting, too, but maybe it's not worth to note it directly in this tutorial. They require only to know what they need to destroy & what to "null" here.
But maybe there really can be a full tutorial all about different types, explainations and thoughts about them.
 

AGD

AGD

Level 13
Joined
Mar 29, 2016
Messages
677
Hmm, setting bj_wantDestroyGroup to true does not automatically destroy groups in any case when using GUI <UnitGroup - Pick units in UnitGroup> actions. It always return a group. You can verify this by looking at the function in jass.

EDIT:
bj_wantDestroyGroup are only used if you want to automatically destroy a group in functions that take a group and returns another group, but it will only destroy the group passed as an argument and the function still returns a new group. Example of these functions are CountUnitsInGroup(), GroupAddGroup(), GroupRemoveGroup(), etc...

I see this being done widely for a long time now by GUIers, and it's only now that I actually checked it and realized this isn't the case.

:oops:
 
Last edited:
Can check converted GUI tomorrow when I'm at home, but from Blizzard.j - WarCraft3 :

Isnt this the GUI ForGroup:
JASS:
function [URL='http://wiki.thehelper.net/wc3/jass/Blizzard.j/ForGroupBJ']ForGroupBJ[/URL] takes [URL='http://wiki.thehelper.net/wc3/jass/common.j/group']group[/URL] whichGroup, [URL='http://wiki.thehelper.net/wc3/jass/common.j/code']code[/URL] callback returns [URL='http://wiki.thehelper.net/wc3/jass/common.j/nothing']nothing[/URL]
   // If the user wants the group destroyed, remember that fact and clear
    // the flag, in case it is used again in the callback.
    local [URL='http://wiki.thehelper.net/wc3/jass/common.j/boolean']boolean[/URL] wantDestroy = [URL='http://wiki.thehelper.net/wc3/bj_wantDestroyGroup']bj_wantDestroyGroup[/URL]
    set [URL='http://wiki.thehelper.net/wc3/bj_wantDestroyGroup']bj_wantDestroyGroup[/URL] = false

    call [URL='http://wiki.thehelper.net/wc3/jass/common.j/ForGroup']ForGroup[/URL](whichGroup, callback)

    // If the user wants the group destroyed, do so now.
    if (wantDestroy) then
        call [URL='http://wiki.thehelper.net/wc3/jass/common.j/DestroyGroup']DestroyGroup[/URL](whichGroup)
    endif
endfunction

And internaly something like this is passed as argument:
JASS:
function [URL='http://wiki.thehelper.net/wc3/jass/Blizzard.j/GetUnitsInRangeOfLocMatching']GetUnitsInRangeOfLocMatching[/URL] takes [URL='http://wiki.thehelper.net/wc3/jass/common.j/real']real[/URL] radius, [URL='http://wiki.thehelper.net/wc3/jass/common.j/location']location[/URL] whichLocation, [URL='http://wiki.thehelper.net/wc3/jass/common.j/boolexpr']boolexpr[/URL] filter returns [URL='http://wiki.thehelper.net/wc3/jass/common.j/group']group[/URL]
   local [URL='http://wiki.thehelper.net/wc3/jass/common.j/group']group[/URL] g = [URL='http://wiki.thehelper.net/wc3/jass/common.j/CreateGroup']CreateGroup[/URL]()
    call [URL='http://wiki.thehelper.net/wc3/jass/common.j/GroupEnumUnitsInRangeOfLoc']GroupEnumUnitsInRangeOfLoc[/URL](g, whichLocation, radius, filter)
    call [URL='http://wiki.thehelper.net/wc3/jass/common.j/DestroyBoolExpr']DestroyBoolExpr[/URL](filter)
    return g
endfunction
^which does normaly return a group, but that will be destroyed later by the ForGroupBJ.

edit: Argh sorry for the broken code. But first one is ForGroupBJ and second one GetUnitsInRangeOfLocMatching.
 
Level 12
Joined
Nov 7, 2014
Messages
566
From tinkering with the handle table, I think it's entries have a single field for storing the reference count of a handle, that is set to 1 upon the creation of the handle, and it gets modified by assignments to variables (global and local, scalar and arrays) and saving/removing the handle from a hashtables, so it seems to me that the section "Difference between local and global" is a bit off?
 
Level 3
Joined
May 12, 2017
Messages
34
Im currently trying to do everything do debug my map and I was asking myself: How many leaks can my map have to assure that nobody gets disconnected etc. I know it depends on more but lets say a classical td without anything around it, so no big deal at its self. I know my map has some issues with leaks and Im not sure if Im skilled enought to remove all of them, thats why Im asking
 
@IcemanBo What about sound?
Sounds can leak. Though, JASS is needed to really create new sounds, which happens automatically in background only once, when used in GUI. So one is safe in GUI.

By the way up to this day I'm still curious if Destructible Groups can leak.
There's no leak like this. There's nothing like a destructible group for storage, but only the mere enumeration of destructibles.
 
Hello IcemanBo, I'm just curious (again) does this function actually leak?

  • Unit Group - Pick all units of type Footman and do
Should I really keep away from using this function? Or is this similar to picking all units in the map and adding condition to only get Footman units?

It is mentioned here (Things That Leak) not to use this function but this wasn't mentioned/missing at the main post, or did I just miss it?
 
The JASS behind needs to be compared for it:
JASS:
function GetUnitsOfTypeIdAll takes integer unitid returns group
    local group   result = CreateGroup()
    local group   g      = CreateGroup()
    local integer index

    set index = 0
    loop
        set bj_groupEnumTypeId = unitid
        call GroupClear(g)
        call GroupEnumUnitsOfPlayer(g, Player(index), filterGetUnitsOfTypeIdAll)
        call GroupAddGroup(g, result)

        set index = index + 1
        exitwhen index == bj_MAX_PLAYER_SLOTS
    endloop
    call DestroyGroup(g)

    return result
endfunction
^it has no object leak inside, but 2 reference leaks.

is this similar to picking all units in the map and adding condition to only get Footman units?
JASS:
function GetUnitsInRectMatching takes rect r, boolexpr filter returns group
    local group g = CreateGroup()
    call GroupEnumUnitsInRect(g, r, filter)
    call DestroyBoolExpr(filter)
    return g
endfunction
^this has 1 reference leak.

So the "PickAllUnitsOfType" has one more reference leak.
Of course it's not good to have more reference leaks, but imo it's not critical. Finally you won't avoid them anyways in GUI, and people used it for ever and without problems.
Lua mode would not have the problem with reference leaks at all btw, as it's fixed there by its nature already... in case it matters for you.

but this wasn't mentioned/missing at the main post,
As there are so many GUI functions with reference leaks included I decided not to give some a special platform, even 2 of ref leaks are included, and just mentioned:
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
 
Level 3
Joined
May 2, 2020
Messages
14
I'm a bit confused about nested functions.

Let's take this artificially convoluted example:

  • Unit - Create 1 Footman for Player 1 (Red) at
    • (Center of (Region(
      • (Random point in (Playable map area)),
      • ((Random point in (Playable map area)) offset by (100.00, 100.00))
    • ))
  • )
  • facing (Player 1 (Red) start location)
How many variables do I need to define (and then remove) here? One can rewrite this action as something like this:

  • Set point_x = (Random point in (Playable map area))
  • Set point_y = (Random point in (Playable map area))
  • Set point_y_offset = (point_y offset by (100.00, 100.00))
  • Set tmp_region = Region(point_x, point_y_offset)
  • Set point_footman = (Center of tmp_region)
  • Unit - Create 1 Footman for Player 1 (Red) at point_footman facing (Player 1 (Red) start location)
Is it different from what would happen if I wrote this instead?
  • Set point_footman = (Center of (Region(
    • (Random point in (Playable map area)),
    • ((Random point in (Playable map area)) offset by (100.00, 100.00))
    • )
  • ))
Another example, inspired by the "Primitives never leak" comment. Consider this:

  • Set int_i = (Number of units in (Units in (Playable map area)))
I suppose that you need to create a temporary variable for UnitGroup (it wouldn't be freed automatically) and "destroying" int_i does not accomplish anything (if it is even possible)?

----------------------

Another question: what's about leaks in conditions? I have a trigger with the following condition:

  • (Level of Some_Ability for (Reviving Hero)) Greater than or equal to 1
do I need to change something here?
 
Last edited:
How many variables do I need to define (and then remove) here?
For now it looks like you maybe meant only to have one random location, So eaither point_x, or point_y, should be enough.
Is a new region required per se, or you only need it for the centered creation? Else, it looks fine:

  • Set point1 = (Random point in (Playable map area))
  • Set point2 = (point1 offset by (100.00, 100.00))
  • Set region = Region(point1, point2)
  • call RemoveLocatin(udg_point1)
  • call RemoveLocatin(udg_point2)
  • --
  • Set point1 = (Center of region)
  • Unit - Create 1 Footman for Player 1 (Red) at point1
  • call RemoveLocatin(udg_point1)
I suppose that you need to create a temporary variable for UnitGroup (it wouldn't be freed automatically) and "destroying" int_i does not accomplish anything (if it is even possible)?
Yes you need to destroy the group, that will be used in condition. Ste the group to a variable, before you use the condition to count units. And destroy after:
upload_2020-5-29_10-0-4.png


... the integer, or counting with "Number of units..." does not leak! Only the "(Units in (Playable map area)) does leak, as it creates a new unit group. So it needs to be changed with variable.

Another question: what's about leaks in conditions? I have a trigger with the following condition:

  • (Level of Some_Ability for (Reviving Hero)) Greater than or equal to 1

do I need to change something here?
That's a pure integer comparisons, which don't leak. And getting the level of ability does not create an internal leak, too. It's leak-safe!
 
Level 3
Joined
May 2, 2020
Messages
14
Thanks!

For now it looks like you maybe meant only to have one random location, So eaither point_x, or point_y, should be enough.
Is a new region required per se, or you only need it for the centered creation?
Let's assume that I need two different random points and a region for some esoteric reason (to make more nested brackets here; I agree that this is not the best way to accomplish this).

I'm still not sure whether
  • Set point = ((Random point in (Playable map area)) offset by (100.00, 100.00))
  • call RemoveLocatin(udg_point)
behaves differently from
  • Set point = (Random point in (Playable map area))
  • Set point_offset = (point offset by (100.00, 100.00))
  • call RemoveLocatin(udg_point)
  • call RemoveLocatin(udg_point_offset)
My real use case looks like this:
  • Set VariableSet Some_Location = ((Position of Some_Unit) offset by (Some_Number, Some_Number))
and I'm wondering if I need to destroy (Position of Some_Unit) in addition to Some_Location


Does (Reviving Hero) leak? I thought that (Triggering Unit) calls a function that leaks. Does (Reviving Hero) behave like (All Players) constant?
 
"(Random point in (Playable map area)" or also "(Position of Some_Unit)" is a point in the end. So if it's directly used, wihout variable + destroy, then it will leak.
Also, if you really need a region, temporary, ensure it's removed, too, once not needed anymore. "call RemoveRect(udg_region)"

"(Reviving Hero)" is just an event response for a trigger, same like "(Triggering Unit)". They're used to retrieve the respective unit, which is needed. Nothing new gets created here, so both don't leak.
 
Top