• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

do dead units leak?

Status
Not open for further replies.
Level 23
Joined
Apr 16, 2012
Messages
4,041
tested with window mode from grimoire
JASS:
function Trig_Untitled_Trigger_001_Actions takes nothing returns nothing
    local group g = CreateGroup()
    local unit u
    local integer i = 0
    call TriggerSleepAction(5.)
    loop
        call GroupAddUnit(g, CreateUnit(Player(0), 'hpea', GetRandomReal(-10000., 10000), GetRandomReal(-10000., 10000), 270))
        set i = i+1
        exitwhen i == 1000
    endloop
    call TriggerSleepAction(10.)
    loop
        set u = FirstOfGroup(g)
        exitwhen u == null
        call GroupRemoveUnit(g, u)
        call KillUnit(u)
        //as well as call RemoveUnit(u)
        set u = null
    endloop
endfunction

//===========================================================================
function InitTrig_Untitled_Trigger_001 takes nothing returns nothing
    set gg_trg_Untitled_Trigger_001 = CreateTrigger(  )
    call TriggerAddAction( gg_trg_Untitled_Trigger_001, function Trig_Untitled_Trigger_001_Actions )
endfunction
my ram always started at 98,800 Kb and when spawned it got to around 114,000 Kb, when I called KillUnit(u) it raised to 124000 Kb and after the units decayed it lowered to 118,980 Kb
Now with plain RemoveUnit(u) it ended on 115,300 Kb, so both KillUnit and RemoveUnit leaks a shit load of memory
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
DSG i did the remove ubit separetly, I didnt call KillUnit and then remove it, i did one test with KillUnit wait till they decays and then did second test with plain RemoveUnit
anyways this is leak which cant be avoided as this is flaw in WC3 engine
exparation timer will only kill the unit(set life to 0) so it will leak if it cant decay(even then it leaks quite a lot as proven by test)
 
That is a terrible leak. In my map, all units are created on the initialization. I have a really specific unit recycling system that doesn't allow an unit to get removed from the game (or truly die) so I have this solved. Summoned units are summoned via triggers so that they get recycled as well. However, if you mean to have your map run for an hour, it will not be a problem, mine is aimed at 10 hours at least, and with a lot of combat, the data would build up.
 
Level 14
Joined
Aug 8, 2010
Messages
1,022
That is a terrible leak. In my map, all units are created on the initialization. I have a really specific unit recycling system that doesn't allow an unit to get removed from the game (or truly die) so I have this solved. Summoned units are summoned via triggers so that they get recycled as well. However, if you mean to have your map run for an hour, it will not be a problem, mine is aimed at 10 hours at least, and with a lot of combat, the data would build up.
10 hours?!?! Dude... u mad!
 
I have played a custom max of 5 hours.
10 hours too crazy.

Play this wreckage. It averages 8 hours.

This is a RPG, though, and its current size is only around 1.8mb, since I have linearized animations, paletted all textures, and squished models. It is madly optimized, and while having around 80 imports, they are still tiny. Since items are controlled via scripts, I actually create all items on initialization, and then show them when you select an unit (it is a singleplayer map). This way I don't even have to deal with item data leaks.
The map is supposed to cap at using 128mb RAM and keep that value, and when testing it I check it.
 
hmm....128 mb? When I started empty map it took 98 Mb of my memory :D
what item data leaks? Im curious :D

I am still in a process of testing this, so I will notify when some more research has been done.

EDIT: I've made the game create 10 items every 0.01 seconds, and then destroy them. This is a stress test, but you leak about 10mb of ram every 30 seconds when doing this, which means, that items leak as well. It is probably the same thing about destructables, though there is rarely ever a reason to create them dynamically.

EDIT2: Destructables seem to leak 15mb/30 seconds when using the same test settings as items. Yeah, this is terrible for maps with great playtime.
 
Level 33
Joined
Mar 27, 2008
Messages
8,035
What about nulling global variables ?
Kingz argued with Magtheridon96 about nulling global variables.

Mag said, the action is proven to be good if object compare is large in quantity.
If the object quantity is like 100+-, don't null the global variables (it's pretty pointless).
But if we're making a script that handles object like 1000++, nulling global variables really helps since the game does not need to keep the data (after finish use).
 
Level 20
Joined
Jul 6, 2009
Messages
1,885
What about nulling global variables ?
Kingz argued with Magtheridon96 about nulling global variables.

Mag said, the action is proven to be good if object compare is large in quantity.
If the object quantity is like 100+-, don't null the global variables (it's pretty pointless).
But if we're making a script that handles object like 1000++, nulling global variables really helps since the game does not need to keep the data (after finish use).

Maybe, but global variables can't leak since you can always reference them.
 
Level 33
Joined
Mar 27, 2008
Messages
8,035
Yeah, I'm talking about "you can still reference them" issues.
That's why I said, nulling global variables really helps since the game does not need to further keep the data in the game engine.

Even if you're not using it/assess it/etc, the game still keeps the data (it is there, but not used), better null them so there's no data left.

But as I said, it's not usually practiced for small scale of object quantity.

Which in rare cases, this "nulling global variables" helps.
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
no need to null globals as proven by this test:
JASS:
globals
    location array l
endglobals


function ABC takes nothing returns nothing
    local integer i = 0
    call BJDebugMsg("start")
    loop
        set l[i]=Location(GetRandomReal(-4000, 4000), GetRandomReal(-4000, 4000))
        call RemoveLocation(l[i])
        set i=i+1
        exitwhen i==1000
    endloop
endfunction

struct AB extends array
    private static method onInit takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterTimerEvent(t, 3., true)
        call TriggerAddAction(t, function ABC)
    endmethod
endstruct
increase the ram by around 100 Kb every time it runs and it creates and destroys 1000 locations
also globals are allocated(arrays spefically) on init and allocates 8192 slots for the array
 
What about nulling global variables ?
Kingz argued with Magtheridon96 about nulling global variables.

Mag said, the action is proven to be good if object compare is large in quantity.
If the object quantity is like 100+-, don't null the global variables (it's pretty pointless).
But if we're making a script that handles object like 1000++, nulling global variables really helps since the game does not need to keep the data (after finish use).

It is a good practice to null global variables if you do not need to use something anymore, of course, but it is not needed, because it will not leak any more memory if you simply set it to another value. The problem with locals (most notably handles, that is) is that they are dynamically created, so they have to be dynamically nulled as well, they won't do that on their own.

Not to mention that global variables are often meant to be set at the game init and not reset during the gameplay. The exception to this are mostly structs, which you should null/set to zeros anyway with destroy methods. However, global variables are not dynamically allocated, which means that they get all the space they need at the map init (I think strings are a bit wonky about this), so I am not sure that resseting their values really does anything, aside from making sure they don't point to the object you've destroyed (set GLOBAL = GetTriggerUnit(), if the unit gets removed, you have to null this global not to reference it anymore, in order to free the handle stack).


Handle variables hold pointers and other types are very basic data so it barely takes any memory.
But you still have to null globals (or change their value, that is) if you destroy the object the handle (our variable) is pointing to, just to free up handle stack space.
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
units leaks by default as proven :D
in JNGP Grimoire there is a field start wc3 with -window
it opens wc3 in a small(not full screen) window so I just open Task Manager(or w/e its called in english) and check the processes(w/e again :D, hope you know what I mean) and I watch the values which Wc3 process takes
 
How do you test the RAM value speed ?

By the way, can you test with units ?
Locations get removed naturally by RemoveLocation, but Units is different story.
Just set Unit[Array] up to 1000 (create 1000 units in the map).

It is about 30mb per 10000 units. Heroes are at least 45mb/10000 heroes, which means that each unit created takes about 3kb of ram. This, of course, depends a lot on how many abilities you've added to the unit, etc. Also, you should still set point/location variables to null once you destroy locations, because of the handle stack.

Also, I do what edo suggested.
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
good idea :D
Results:
JASS:
globals
    unit array u
endglobals


function ABC takes nothing returns nothing
    local integer i = 0
    call BJDebugMsg("start")
    loop
        set u[i]=gg_unit_hpea_0000
        set i=i+1
        exitwhen i==1000
    endloop
endfunction

struct AB extends array
    private static method onInit takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterTimerEvent(t, 3., true)
        call TriggerAddAction(t, function ABC)
    endmethod
endstruct
"leaked" 4 kb memory per run :D so I dont think its really needed to null globals
 
Level 20
Joined
Jul 6, 2009
Messages
1,885
@HappyTauren,
If nulling variables frees up handle stack space, shouldn't this display same number twice?
JASS:
    function init takes nothing returns nothing
        set l = Location(500.0, 500.0)
        call BJDebugMsg(I2S(GetHandleId(l)))
        call RemoveLocation(l)
        set l = null
        call BJDebugMsg(I2S(GetHandleId(Location(100.0, 200.0))))
    endfunction

EDIT: Nevermind, it actually does if I create the second location in another function.
 
@HappyTauren,
If nulling variables frees up handle stack space, shouldn't this display same number twice?
JASS:
    function init takes nothing returns nothing
        set l = Location(500.0, 500.0)
        call BJDebugMsg(I2S(GetHandleId(l)))
        call RemoveLocation(l)
        set l = null
        call BJDebugMsg(I2S(GetHandleId(Location(100.0, 200.0))))
    endfunction

EDIT: Nevermind, it actually does if I create the second location in another function.

Yes, the new handle gets an old handle id in case you create it in another thread.


this is getting reallllly off topic, this has nothing to do with dead units leaking anymore :D
you have point, its impossible to clear unit leaks but its proven that nulling globals is next to useless

You couldn't be more wrong, and I will provide a script to prove why.
 
Level 20
Joined
Jul 6, 2009
Messages
1,885
Ok, apparently
JASS:
    function tuturu takes nothing returns nothing
        set l = Location(100.0, 200.0)
        call BJDebugMsg(I2S(GetHandleId(l)))
    endfunction

    function init takes nothing returns nothing
        call TimerStart(CreateTimer(), 1.00, false, function tuturu)
        set l = Location(500.0, 500.0)
        call BJDebugMsg(I2S(GetHandleId(l)))
        call RemoveLocation(l)
        set l = null
    endfunction
this will show same numbers, however, if I remove the nulling of l, the handle id goes up.
Maybe nulling globals is necessary.
 
Nulling global handles (or setting them to a different value) is always necessary if you destroy the object a handle points to. If you set your global location handle to a location you've just created, then destroy location, but don't set your global handle to null, it will occupy the handle id slot until you close the map. However, should you set it to null, even half an hour after you've destroyed the variable, it still frees a handle slot.

Here's how much data dead units will leak. This script will run for 60 seconds, create 60000 objects (1000 units per second) and it will probably leave a leak of 6mb (6mb is good, considering there are around 60000 units that are permanently leaking!). Dead units automatically get removed after their decays, this just shows this process really fast. It doesn't kill units, but remove them instantly, which is something that usually happens 60 seconds after death for most units. I've got wrong results because my benchmarking script was not optimized, but now it is 6mb for 60000 leaking units, which makes it 0.1kb per unit, which is awesome. After this test, I no longer thing this is a real issue, just remember to null all handles pointing to an unit when it dies (or when it gets removed, that is).

JASS:
scope LeakTest initializer Init
globals
    private integer count    = 0
    private integer countrun = 0
    private player owner = Player(0)
endglobals

private function Run takes nothing returns nothing
    loop
        set count = count + 1
        exitwhen count > 10
        //call RemoveItem(CreateItem('gcel', 0, 0))
        call RemoveUnit(CreateUnit(owner, 'hpea', 0, 0, 0))
        //call RemoveDestructable(CreateDestructable('LTbs', 0, 0, 0, 1, 1))
    endloop
    
    set count = 0
    set countrun = countrun + 1
    
    if countrun >= 6000 then
        call BJDebugMsg("60000 objects were created, check the memory used now!")
        call PauseTimer(GetExpiredTimer())
        call DestroyTimer(GetExpiredTimer())
    endif
endfunction

private function Init takes nothing returns nothing
    call TimerStart(CreateTimer(), 0.01, true, function Run)
endfunction
endscope
 
Apparently, Blizzard isn't changing their "referenced" boolean in whatever map they're using for the variables when you change the value of a global, so it still counts as referenced.

You need to null globals before you change their value just to fix this bug.

I'm only asserting this, I'll see if this is actually true later when I'm not lazy.
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
so if I do
JASS:
globals
    location l
endglobals

function ABC takes nothing returns nothing
    set l = Location(200., 500.)
    call RemoveLocation(l)
    set l = Location(100., 150.)
    call RemoveLocation(l)
    set l = null
endfunction
free up both handles or only the last one? Or do I have to do set l = null twice? :D Im being curious now
 
Apparently, Blizzard isn't changing their "referenced" boolean in whatever map they're using for the variables when you change the value of a global, so it still counts as referenced.

You need to null globals before you change their value just to fix this bug.

I'm only asserting this, I'll see if this is actually true later when I'm not lazy.

Please do test this. This is as retarded as it is intriguing.

EDIT: You're right :( This is so sad. Global handles do seem to leak if you do not null them before setting them to a new value. Woah.
 
Status
Not open for further replies.
Top