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

[vJASS] About Memory Leaks

Status
Not open for further replies.
I have read the sticky thread Things that Leaks and Silvenon mentioned that leak is a serious problem in spells that why I want to check for leaks in this script
JASS:
scope AoETemplate initializer Init
 
    private keyword Data
 
    globals
        // Constants
        private constant real TIMER_INTERVAL = 0.015
        private constant real DIST = 16.00
        private constant real NOVA_RADIUS = 544.00 // radius of the Nova
        private constant integer NOVA_SIZE = 36 // number of the Nova Units
        private constant real dA = 360/NOVA_SIZE
        private constant integer UNIT_ID = 'e001' // rawcode of the Nova Unit
        private constant integer ABIL_ID = 'A001' // rawcode of the Dummy Spell
        private constant attacktype ATKTYPE = ATTACK_TYPE_MAGIC
        private constant damagetype DMGTYPE = DAMAGE_TYPE_FIRE
        // Note: Changing the following Constants will result in different damage value
        // Make sure if you change this values to change the dummy spell damage description as well
        private constant real AB_CONST_FACTOR = 50.00
        private constant real AB_LEVEL_FACTOR = 50.00
        private constant real AB_PREV_LVL_FACTOR = 1
        // if the previous constant is set to 0 the damage increment will be arithmatic
        // if 1 it will take the damage that the spell deals in the previous level and add AB_CONST_FACTOR + AB_LEVEL_FACTOR * AbilityCurrentLevel
        // Variables
        private timer tm = CreateTimer()
        private Data array temp_dat
        private integer index = 0
        private Data iFilter // Filter Data
        private boolexpr fFilter // Filter function
        private group TempGroup = CreateGroup()
    endglobals
 
    function Damage takes integer al returns real
        local real prevDmg
        if al == 1 then
            set prevDmg = 0
        else
            set prevDmg = Damage.evaluate(al -1)
        endif
        return AB_CONST_FACTOR + AB_LEVEL_FACTOR * al + AB_PREV_LVL_FACTOR * prevDmg
    endfunction
 
    private struct Data
        unit Caster
        unit array Nova[NOVA_SIZE]
        real expand
        real Cx
        real Cy
        player Owner
        group dGroup = CreateGroup() // Damaged Units Group
        real dmg
 
        //static method Damage takes integer AbiLevel returns real
            //local real prev
            //if AbiLevel == 1 then
                //set prev = 0
            //else
                //set prev = Damage.evaluate(AbiLevel - 1)
            //endif
 
        //return AB_CONST_FACTOR + AB_LEVEL_FACTOR * AbiLevel + AB_PREV_LVL_FACTOR * prev
        //endmethod
 
        static method NovaExpand takes nothing returns nothing
            local thistype dat
            local integer i = 0
            local integer j
            local real x
            local real y
            local real a
            local unit u
 
            loop // this loop cycles through all the casters which have cast the spell
                exitwhen i >= index
                set dat = temp_dat[i]
                // Nova Expanding
                set j = 0
                set a = 0
                set dat.expand = dat.expand + DIST
 
                loop
                    exitwhen j >= NOVA_SIZE
                    set x = dat.Cx + dat.expand * Cos( a * bj_DEGTORAD )
                    set y = dat.Cy + dat.expand * Sin( a * bj_DEGTORAD )
                    call SetUnitX( dat.Nova[j] , x )
                    call SetUnitY( dat.Nova[j] , y )
                    set j = j + 1
                    set a = a + dA
                endloop
            // Damage Units that the nova has reached
                set iFilter = dat
                call GroupEnumUnitsInRange(TempGroup, dat.Cx, dat.Cy, dat.expand, fFilter)
 
                loop
                    set u = FirstOfGroup(TempGroup)
                    exitwhen u == null
                    call GroupAddUnit(dat.dGroup, u)
                    call UnitDamageTarget(dat.Caster, u, dat.dmg, false, false, ATKTYPE, DMGTYPE, null)
                    call GroupRemoveUnit(TempGroup, u)
                endloop
 
                if dat.expand >= NOVA_RADIUS then // check if the nova has reached its max AoE
                    set index = index - 1
                    set temp_dat[i] = temp_dat[index]
                    set i = i - 1
                    call dat.destroy()
                endif
 
                set i = i + 1
            endloop
 
            set u = null
 
        endmethod
 
        static method create takes unit caster returns Data
            local thistype dat = thistype.allocate()
            local integer i = 0
            local real A = 0.00
            set dat.Caster = caster
            set dat.Owner = GetOwningPlayer(dat.Caster)
            set dat.Cx = GetUnitX(dat.Caster)
            set dat.Cy = GetUnitY(dat.Caster)
            set dat.dmg = Damage(GetUnitAbilityLevel(dat.Caster,ABIL_ID))
            // the following function displays a msg of the amount of damage that the spell deals
            // make sure when you use it to remove the following function or add "//" before it
            //call DisplayTimedTextToPlayer(dat.Owner,0,0,10,"|cffff0000" + R2S(dat.dmg) + "|r")
 
            loop
                exitwhen i >= NOVA_SIZE
                set dat.Nova[i] = CreateUnit( dat.Owner , UNIT_ID , dat.Cx , dat.Cy , A )
                set A = A + dA
                set i = i + 1
            endloop
 
            if index == 0 then
                call TimerStart(tm,TIMER_INTERVAL,true,function Data.NovaExpand)
            endif
 
            set temp_dat[index] = dat
            set index = index + 1
            return dat
        endmethod
 
        static method FilterUnit takes nothing returns boolean
            return GetWidgetLife(GetFilterUnit()) >= .305 and IsUnitEnemy(GetFilterUnit(),iFilter.Owner) and not IsUnitType(GetFilterUnit(),UNIT_TYPE_MAGIC_IMMUNE) and not IsUnitInGroup(GetFilterUnit(),iFilter.dGroup)
        endmethod
 
        method onDestroy takes nothing returns nothing
            local integer i = 0
            loop
                exitwhen i >= NOVA_SIZE
                call KillUnit(.Nova[i])
                set .Nova[i] = null
                set .expand = 0
                set i = i + 1
            endloop
 
            if index == 0 then
                call PauseTimer(tm)
            endif
 
        endmethod
 
    endstruct
 
        private function Conditions takes nothing returns boolean
            return GetSpellAbilityId() == ABIL_ID
        endfunction
 
        private function Actions takes nothing returns nothing
            call Data.create(GetTriggerUnit())
        endfunction
 
        private function Init takes nothing returns nothing
            local trigger t = CreateTrigger()
            call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
            call TriggerAddCondition(t,Condition(function Conditions))
            call TriggerAddAction(t,function Actions)
            set fFilter = Filter(function Data.FilterUnit)
        endfunction
 
endscope

The timer leaks and the dummy units will leak also the groups will leak. But how can remove all this leaks if they are caused ?
 
Level 16
Joined
Feb 22, 2006
Messages
960
Use the onDestroy method and put every stuff u want to desroy or nullify in there =>

JASS:
method onDestroy takes nothing returns nothing
     call DestroyXXX(this.XXX)
     set this.XXX = null
endmethod

everytime the destructor is called (destroy) this method is called... by default it's empty
and you can overwrite it
 
Level 29
Joined
Jul 29, 2007
Messages
5,174
They themselves can't leak, since they are merely integers, but they still hold an index if you do not call their destructor.

JASS:
struct A
endstruct

struct B
    A a

    static method create takes nothing returns nothing // or whatever the syntax is
         // ops, you never remove this, 1 index less for your A structs
        .a = A.create()
    endmethod
endstruct
 
Level 21
Joined
Aug 21, 2005
Messages
3,699
Variables used in structs are not destroyed and need to be destroyed manually if you want to.
Unlike local variables, struct members do NOT need to be nulled.

To give an example:

JASS:
struct Test
    private location loc

    private method onDestroy takes nothing returns nothing
        call RemoveLocation(this.loc)
    endmethod
endstruct
 
I never said that they wouldn't recycle be not nulling them... they just recycle faster, maybe faster is the wrong word, because if you do not null it the id can be used some time later again, but if you null it the id is released earlier
It's not faster, it's not more efficient, it makes no difference whatsoever. I guess you could say it's slower since the operation of nulling it might take a nanosecond or two...
 
Level 29
Joined
Jul 29, 2007
Messages
5,174
Because you people went all off topic:

1) You should destroy things in a struct the same way you need to destroy them out side of a struct.

JASS:
struct A
    location l = Location(0,0,0)

    static method onDestroy takes nothing returns nothing // or whatever the syntax is
        call RemoveLocation(.l)
    endmethod 
endstruct

2) You should call the destroy() method of member structs, or else their id is forever used and you have one less index.

JASS:
struct B
endstruct

struct A
    B b = B.create()

    static method onDestroy takes nothing returns nothing // or whatever the syntax is
        call b.destroy()
    endmethod 
endstruct

3) Struct members are globals, so there's no point nulling them. There's nothing such as "you can use them earlier". You can use them when their struct index is freed, and you're making a new struct.
 
Level 16
Joined
Feb 22, 2006
Messages
960
here a quote like I learned it from dr super good... I think he knows what he say

You do not have to null struct members if you recycle them quickly, otherwise you do.

This is purly to free up more handle indexes quicker and remember that nulling takes near no time at all. Basically globals do not automatically free the handle index like locals, however they can not leak handle indexes unlike locals. Thus nulling them lets the indexes be recycled, while otherwise if the value of a handle in an array is never overwritten, it clogs up that handle index forever (eg if for some reason you used 30 structs at one point in the game while after that the max you used only was 10, 20 indexes worth of handles would be clogged up unnescescarily).
 
Status
Not open for further replies.
Top