• 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.

Things That Leak

I can verify that the ids are blocked. Since it did not cause a desync (game kill) in my current test, which surprises me too at the moment, I wonder if objects/agents have another id they use for net-traffic that is not exposed to jass (which of course may alter the life cycle).
Which basicly brings us back to the point admitting that some things in WC3 are a deadly mixture of magic and insanity.
This or WC3 is just plain spaghetti coding at its finest. :/
 
there is finite number of handles you can have in handle table before crashing the game, it is however somewhere around several milions of handles

Of course, but I'm sure it's not hardcoded (unless someone tried maxing it out before =o)

I don't have enough RAM to test for such a limit.
It will be no more than 2.146 billion given infinite RAM (it can be ~4.293 billion, but who knows what type modifiers they're using in their actual source?), but who's to say they didn't hardcode a limit less than that? ;p
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
when I was just like: I will make wc3 take all my ram(4 giga because of 32 bit OS) it went only up to 1,6 giga aprox and crashed

I was generating 1 point leak and 1 unit without killing it at random position on the map(GetRandomReal(..., ...) with every 0.00 seconds timer
 
That's approximately 3.9 million handles =o (Not sure - Depends on the size of a unit in memory. Some sources tell me 3KB, others tell me 44 bytes >.<)

I guess the limit might be close to 1 million if it's present? =o

My record for filling up my RAM with Wc3 is different
I generated 1GB of leaks with locations. Knowing that a location is 361 bytes give or take 10 bytes, that's about 2.974 million handles.

There might not actually be a limit for the handles hardcoded internally.
The crash might be caused by some other factor.
 
when I was just like: I will make wc3 take all my ram(4 giga because of 32 bit OS) it went only up to 1,6 giga aprox and crashed

I was generating 1 point leak and 1 unit without killing it at random position on the map(GetRandomReal(..., ...) with every 0.00 seconds timer
I'm curious, how did you do that? WC3 does only allow creating a certain number of units (either 4k or 8k, i don't remember) until some units must be removed/killed first.
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
well, the crash maybe happened becuase I had no RAM left or something like that

I did simple GUI trigger with 1 action - Create 1 unit of type footman at position(Random position at(Playable map area))

with event every 0.00 second

and I tested this again with only doing(very similar):
JASS:
scope myscope initializer i
     private function run takes nothing returns nothing
         call Location(GetRandomReal(-1000, 1000), GetRandomReal(-1000, 1000))
     endfunction

    private function i takes nothing returns nothing
        call TimerStart(CreateTimer(), 0.00, true, function run)
    endfunction
endscope

and funny enough, it seemed like Wc3 actually tried to force remove the leaks because at 1 GB it started dropping a bit and after 1.2 GB it popped error: no CD disk detected in driver or w/e and crashed :D
 
mine crashes around the same number. i believe its the max ram that wc3 can handle my pc has 16 gigs of ram and can use all of it. i have windows 7 64 bit ultimate and i have maxed out the ram b4 using rendering software. wc3 crashed around 1.6 to 1.8gb. i havent noticed a limit on the units but i havent sat there and counted them. i added a unit to map and increased an integer to count units on map. making 100 units every .01 seconds i got around 20k for the integer until it finally crashes. i did clean up the point leak w that test
 
Level 4
Joined
Jan 27, 2010
Messages
133
Yeah, so, I tested nulling globals vs. not nulling globals using Maker's test code. Conclusion: null globals nulling globals might prevent a very, very insignificant leak. As Zwiebelchen rightly points out below, it should never matter (the test created and destroyed 60 MILLION locations, and memory usage only went up a few megabytes).




Timers createdMemory Consumption
2 702 50088.1M
4 585 00088.3M
6 407 00088.6M
8 240 00087.7M
11 746 50083.3M
13 539 50089.7M
33 932 00091.0M
40 913 50090.4M
58 903 00092.2M

The trend is clearly rising in the long term. Just like Maker said, it's a really minor leak (considering the code is creating 500x100=50000 locations every second).

JASS:
library leakTest initializer onInit
    globals
        private timer t = CreateTimer()
        private constant integer LOCS = 500
        private integer c = 0
    endglobals

    private function leaks takes nothing returns nothing
        local integer i = LOCS
       
        loop
            exitwhen i == 0
            set udg_loc = Location(0,0)
            call RemoveLocation(udg_loc)
            // set udg_loc = null
            set c = c + 1
            set i = i - 1
        endloop
    endfunction
   
    private function pauseResume takes nothing returns nothing
        if ModuloInteger(GetTriggerExecCount(GetTriggeringTrigger()) , 2) == 1 then
            call BJDebugMsg("started")
            call TimerStart(t, 0.01, true, function leaks)
        else
            call BJDebugMsg("paused, " + "locs created: " + I2S(c))
            call PauseTimer(t)
        endif
    endfunction

    private function onInit takes nothing returns nothing
        local trigger trg = CreateTrigger()
        call TriggerRegisterPlayerEvent(trg, Player(0), EVENT_PLAYER_END_CINEMATIC)
        call TriggerAddAction(trg, function pauseResume)
    endfunction
endlibrary




Timers createdMemory Consumption
4 010 50085.5M
7 815 00084.8M
12 700 00084.9M
25 000 00084.9M
34 800 00085.1M
42 000 00085.5M
48 600 00085.9M
55 000 00083.3M
59 400 00082.7M

There is no rising trend here...

JASS:
library leakTest initializer onInit
    globals
        private timer t = CreateTimer()
        private constant integer LOCS = 500
        private integer c = 0
    endglobals

    private function leaks takes nothing returns nothing
        local integer i = LOCS
       
        loop
            exitwhen i == 0
            set udg_loc = Location(0,0)
            call RemoveLocation(udg_loc)
            set udg_loc = null
            set c = c + 1
            set i = i - 1
        endloop
    endfunction
   
    private function pauseResume takes nothing returns nothing
        if ModuloInteger(GetTriggerExecCount(GetTriggeringTrigger()) , 2) == 1 then
            call BJDebugMsg("started")
            call TimerStart(t, 0.01, true, function leaks)
        else
            call BJDebugMsg("paused, " + "locs created: " + I2S(c))
            call PauseTimer(t)
        endif
    endfunction

    private function onInit takes nothing returns nothing
        local trigger trg = CreateTrigger()
        call TriggerRegisterPlayerEvent(trg, Player(0), EVENT_PLAYER_END_CINEMATIC)
        call TriggerAddAction(trg, function pauseResume)
    endfunction
endlibrary

13 800 00082.5M
37 000 00082.8M
59 000 00082.5M

And this is horribly confusing! Is WC3-functions somehow making extra memory for globals, which is only released upon nulling? I really expected this to be 499/500 as "leaky" as the not nulling version...
JASS:
library leakTest initializer onInit
    globals
        private timer t = CreateTimer()
        private constant integer LOCS = 500
        private integer c = 0
    endglobals

    private function leaks takes nothing returns nothing
        local integer i = LOCS
       
        loop
            exitwhen i == 0
            set udg_loc = Location(0,0)
            call RemoveLocation(udg_loc)
            set c = c + 1
            set i = i - 1
        endloop
        set udg_loc = null
    endfunction
   
    private function pauseResume takes nothing returns nothing
        if ModuloInteger(GetTriggerExecCount(GetTriggeringTrigger()) , 2) == 1 then
            call BJDebugMsg("started")
            call TimerStart(t, 0.01, true, function leaks)
        else
            call BJDebugMsg("paused, " + "locs created: " + I2S(c))
            call PauseTimer(t)
        endif
    endfunction

    private function onInit takes nothing returns nothing
        local trigger trg = CreateTrigger()
        call TriggerRegisterPlayerEvent(trg, Player(0), EVENT_PLAYER_END_CINEMATIC)
        call TriggerAddAction(trg, function pauseResume)
    endfunction
endlibrary
 
Last edited:
Yeah, so, I tested nulling globals vs. not nulling globals using Maker's test code. Conclusion: null globals.
If we compare this to the unfixable leaks of properly removed units and special effects, this goes into diminishing returns.

I definitely won't go through all the hassle to null my globals just for a megabyte of memory saved after a 3 hour game...

The conclusion should more be:
If you are paranoid, null globals. If you live in the real world, save the time.

And to be honest, I don't even think it's a real leak. More like increasing memory allocation of WC3.
Which means, WC3 probably doesn't always use the same memory adress for a global variable, but changes its adress (possibly due to internal garbage collection) from time to time.
This would mean that WC3 allocates more memory that is just not used yet, but might be used later on.

This is like making a parking lot bigger. You don't have more cars on it, but you could have more cars in it if you wish. That doesn't mean that new cars can not get on the empty spots if the area of the parking lot is enlarged (which would be the case with 'real' leaks). I hope you get the idea...

I'd take that guess a step further by saying, that this is probably caused by your operating system! Operating systems use optimizing strategies so that to reallocate memory for faster reading that is used more frequently. Remember that your nulling operation basicly increases the amount of memory operations by a half. This might affect your OS aswell or the memory allocation of WC3!
The reason I'm saying this is that the rise of memory is so goddamn low, that it's hard to say wether this is an actual memory leak or caused by overhead of WC3 or your OS we don't have any control over (and isn't harmful in any way).
Actually, the random fluctuation of memory usage of WC3 is much larger than the increase seen... (look for example at the sudden drop to 83 megs at 11.000.000 ...)


And looking at the numbers, I feel we should stop making all the less experienced users out there, especially the GUIers worried about nulling globals.
You need literally BILLIONS of global reassignements for a few megabytes of memory. Really, there are far worse things than that you don't even have control over...
 
Last edited:
Level 4
Joined
Jan 27, 2010
Messages
133
And to be honest, I don't even think it's a real leak. More like increasing memory allocation of WC3.
Which means, WC3 probably doesn't always use the same memory adress for a global variable, but changes its adress (possibly due to internal garbage collection) from time to time.
This would mean that WC3 allocates more memory that is just not used yet, but might be used later on.

... parking lot...

...The reason I'm saying this is that the rise of memory is so goddamn low, that it's hard to say wether this is an actual memory leak or caused by overhead of WC3 or your OS we don't have any control over (and isn't harmful in any way).

Quite possible! I just find it fascinating that it would do this when not nulling, but not do this when nulling (even if nulling only at end of function). I mean, the values fluctuated a bit over time, but the general trend of the not nulling one... WC3 is such a magic box sometimes...


And looking at the numbers, I feel we should stop making all the less experienced users out there, especially the GUIers worried about nulling globals.
You need literally BILLIONS of global reassignements for a few megabytes of memory. Really, there are far worse things than that you don't even have control over...

Definitely.
 
only thing is its not that much extra effort to null the variables so y not do it lol. i can understand its not worth going over every one of ur old triggers to do it but when making new ones y not.

Well, there is a tradeoff for everything. Remember that for every extra character in your code, you are adding a slight boost to the file size and a slight speed decrease. Of course, this is all on a microscopic level, but so is the RAM "leak".

The main reason why we null locals is because it will allow the handle ID to be recycled. Since that problem doesn't exist with globals, we don't have to really worry about it.

You can still null globals if you want to, but it is more of a matter of personal preference.
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
nothing special, it just nullifies the memory variable is pointing to(the value stored in variable) so if you call some functions which take that type and you pass global, it will not do anything

Be aware that nullying(I dont know about the y tho) is not the same as destroying the handles
 
Level 20
Joined
Apr 14, 2012
Messages
2,901
nothing special, it just nullifies the memory variable is pointing to(the value stored in variable) so if you call some functions which take that type and you pass global, it will not do anything

Be aware that nullying(I dont know about the y tho) is not the same as destroying the handles

I never knew that info about Globals until now, but I sure know the difference between nulling a handle and destroying it.

Thanks for answering me =)
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
yes, the enumeration is bit heavy operation, but create/destroy unit group is lot more heavy, if you create, fill,, itterate, destroy group every .01 sec it may and most likely will start dropping fps just because of how heavy the create destroy combo is

you can just add units to the unit group when spell is cast and remove them when the effect of the spell has ended for them, then when the group is empty you should turn off the trigger to prevent useless iterations, but you most likely know that already
 
Level 26
Joined
Aug 18, 2009
Messages
4,099
bj_lastCreatedGroup is
  • Unit Group - Last Created Unit Group
right?

Not quite:

JASS:
function GetLastCreatedGroupEnum takes nothing returns nothing
    call GroupAddUnit(bj_groupLastCreatedDest, GetEnumUnit())
endfunction

function GetLastCreatedGroup takes nothing returns group
    set bj_groupLastCreatedDest = CreateGroup()
    call ForGroup(bj_lastCreatedGroup, function GetLastCreatedGroupEnum)
    return bj_groupLastCreatedDest
endfunction
 
Level 6
Joined
Jul 21, 2011
Messages
139
u should also create one group for spell. as creating it every iteration is very inefficient.
u also wont have to destroy it anymore.

yes, the enumeration is bit heavy operation, but create/destroy unit group is lot more heavy, if you create, fill,, itterate, destroy group every .01 sec it may and most likely will start dropping fps just because of how heavy the create destroy combo is

you can just add units to the unit group when spell is cast and remove them when the effect of the spell has ended for them, then when the group is empty you should turn off the trigger to prevent useless iterations, but you most likely know that already

please upload a map with a spell example, im interesed
 
Level 8
Joined
Oct 2, 2013
Messages
288
Re-using or changing a var doesn't leak right?

For instance:
I set a var to a point -> spawn a unit at that point throught the var -> change the var to a new location/point -> spawn another unit at that new location through the var.

As long as I reuse the var it's not considered leaking, yes?
 
Yes it is.
Variables are not leaking, objects (like locations) are.
If you change the variable to a new location, then you're creating a new location without clearing the old one : it leaks.

There is an action for moving locations (and do what you want) but it's not available in the normal editor, only is UMSWE and such (some people say it bugs but I never experienced bugs from it myself). Unless you do that, you should remove the leak for each new object you create.
 
Level 20
Joined
Jul 6, 2009
Messages
1,885
Re-using or changing a var doesn't leak right?

For instance:
I set a var to a point -> spawn a unit at that point throught the var -> change the var to a new location/point -> spawn another unit at that new location through the var.

As long as I reuse the var it's not considered leaking, yes?

It leaks. It's not about the variable, it's about the object it stores. By changing what location your variable stores, you can no longer access previous location but it's still there. So before changing variables that point to an object you won't use anymore, destroy the object (location in this case).
 
Level 8
Joined
Oct 2, 2013
Messages
288
Holy ship, I didn't expect an answer so fast! :O
And entire 3 great answers! This is overwhelming.

Thank you all so much! I hope you will be around for more of my noobish questions :)

Now I've basicly done it like this:

A var defines a point/location -> I spawn a unit through the var so it appears on that location -> i remove the location -> define a new location with the same var -> spawn another unit through the var -> delete the location once again

So I guess it doesn't matter how much or what I spawn at the location through the var? As long as the var doesn't change position I can always delete the location with one action?
 
Last edited:
Level 6
Joined
Jan 15, 2010
Messages
163
Holy ship, I didn't expect an answer so fast! :O
And entire 3 great answers! This is overwhelming.

Thank you all so much! I hope you will be around for more of my noobish questions :)

Now I've basicly done it like this:

A var defines a point/location -> I spawn a unit through the var so it appears on that location -> i remove the location -> define a new location with the same var -> spawn another unit through the var -> delete the location once again

So I guess it doesn't matter how much or what I spawn at the location through the var? As long as the var doesn't change position I can always delete the location with one action?

Vas ist correct! As long as you dont change the value association, you will not create any more memory faults. And dont remove every time the point, or it wont work the next time and you will need to create it once again!
Every time you chose a location, its like WC3 editor is auto-creating hidden vars and thats why you need to associate em to your own var, so you can clear em... Sounds dull and complicated?
 
Level 8
Joined
Oct 2, 2013
Messages
288
Not at all, sounds very good thanks^^

I feel I have a good understanding of it now. I even did some testing just to be clear.

I was wondering about special effects attached to units. I'm aware you need to destroy special effects, but does it leak any points like "create special effect at point" does?
 
(....)
There is an action for moving locations (and do what you want) but it's not available in the normal editor, only is UMSWE and such (some people say it bugs but I never experienced bugs from it myself). Unless you do that, you should remove the leak for each new object you create.
UMSWE never bugs, at least the 5.0 Version.
Only note is to never use triggers under Compatibility Category
 
Level 8
Joined
Oct 2, 2013
Messages
288
If I define: Var = (Random unit from Unit Group) and I set the unit group to (Units within range of a Point).
Would that leak both a group and a point?
Do I solve that by creating a Var for both UG and Point and destroying them afterwards?
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
yes, because if you choose from the gui menu, you pretty much always create new group
every time you see Point(…, …), it must be assigned to var or it will leak

you dont technically need group var however, you could use
Custom Script: set bj_wantDestroyGroup = true
and that should clean the group after the enumeration you do
 
Top