| 04-30-2006, 08:42 PM | #1 |
Hi well i readed the leak fixing tutorials and im trying to know if i undestand the problem. - Frist of all only the "handle" related variables leak. - Second, cleaning leaks of temporal objects like UnitGroup it easy and those are tthe most common leaks (points also). - Third and this is the worst one GUI functions like TriggeringUnit() leak (in jass GetTriggerUnit()). When i first strated to make maps i realy didnt even know about JASS or memory leaks until i realized that for some reason ppl was droping from my map. So after that i started to learn how to fix GUI leaks by storing points in vars or groups, but now and for the third reason i listed above i realize that fixing those leaks was like fixing 1% of the real problem, because in GUI the use of things like TriggeringUnit(), ItemBeingManipulated() generate leaks for not NULLING variables. This is a sample of one acction in my map: Trigger: Set GroupTeleport[(Player number of (Owner of (Triggering unit)))] = (Units within 1000.00 of (Position of (Triggering unit)) matching (((Matching unit) is A Hero) Equal to True))Well yes im fixing the leak of the group by storing it (i release it later by the usage of DestroyGroup()) but in fact the function GetTriggerUnit() is leaking so im still messing up. If i have this simple function: JASS:function func takes nothing returns nothing call DisplayTextToForce( GetForceOfPlayer(GetOwningPlayer(GetManipulatingUnit())), "This function leaks a lot") endfunction that will leak right so to fix it i would have to do this: JASS:function func takes nothing returns nothing local unit u = GetManipulatingUnit() local force f = GetForceOfPlayer(GetOwningPlayer(u)) call DisplayTextToForce( f, "This function isnt leaking") call DestroyForce(f) set f = null set u = null endfunction i suppose thats fine, but im wondering if GetOwningPlayer(u) leaks, and in case it does how do i clear it? Am i missing something in the fixed function? I dont have to clean the objects of the functions like GetTriggerUnit() right? (i imagina that doing it would end in removing the unit from the game) but i do have to nulify the value right? so i will nead to declare locals per every function of that kind that i need to use. My final question is, whats a boolexpr? for example can i call the folowing code a boolexpr? JASS:If (i > 10) I know that, that code returns a bool, which dont need to be nulify (types that dont need to be nulifyed: string, real, integer, code, boolean) but in the case that the "i > 10" were a boolexpr do i need to store it in a local var to be able to "DestroyBoolExpr(someBoolExpr)" later and that way aboid it from leaking? In my opinion if all the things that i listed are leaks i just cant undestand how blizzard did such thing, i mean im a programmer (C, C++, VB.NET, Java, and others) this kind of bugs make me thingk twise about learning JASS because it makes imposible for the programmer to make a leak clean code, even in C++ i dont have to clean up so many things this is terrible, i wonder why WC3 dont has a Garbage Collector. No offence to al the ppl that realy like this code, in fact it makes many things posible and thats the reason why i want to learn it. Thanks a lot, i hope i can learn to be a good JASS programmer. Please correct me if im wrong in something. PS: sry if i posted this in the wrong place, i tryed to post it under the leak tutorial section but no one answered. |
| 04-30-2006, 08:54 PM | #2 |
in your first example: Position of (Triggering unit) That leaks a location. You have to store the location in a variable, reference the variable in your Set GroupTeleport function and then RemoveLocation the point. |
| 04-30-2006, 08:58 PM | #3 | |
Player groups, points and unit groups leak as variables. Special effects, dummy units, and regions leak if not destroyed, but the variables don't. Strings also leak, but that is unstoppable and negligable. Globals do not need to be nullified. Nullifying stuff has no impact on leaks in GUI, only on locals. Quote:
Doesn't leak. JASS:function func takes nothing returns nothing local unit u = GetManipulatingUnit() local force f = GetForceOfPlayer(GetOwningPlayer(u)) call DisplayTextToForce( f, "This function isnt leaking") call DestroyForce(f) set f = null set u = null endfunction No point in setting GenManipulatinUnit() to a variable. I don't know where you got the idea that native functions as basic as GetTriggerUnit() would leak, but JASS isn't that bad. |
| 04-30-2006, 09:02 PM | #4 |
for the "If (i > 10)" question. youll have to put "then" after it to work. "If (i > 10) then" I'm also not sure if it matters if "if" is "If" or "if". Use "if" to be sure. |
| 04-30-2006, 09:16 PM | #5 |
well in fact the info about the natives leaking i got them from the tutorial in here and in WC3JASS.COM |
| 04-30-2006, 09:21 PM | #6 |
theres the other tutorial, one of the functions that return a unit for example is: GetManipulatingUnit() where unit is child of handle so it leaks and u must set it to null, and thats a native function. http://www.wc3jass.com/viewtopic.php?t=2072 Edited by Blade.dk. Reason: Fixed tags. |
| 04-30-2006, 09:26 PM | #7 | ||
Quote:
All locals have to be nullified, but as GUI doesn't use locals, GUI won't leak if you don't nullify them. It says that in the article: Quote:
Oh, and don't double post. Edit instead. |
| 05-02-2006, 09:02 PM | #8 |
I been looking at the blizzard.j file that has functions that as it says at the top, are needed in every map script, well i have a sample function here: JASS:function ReplaceUnitBJ takes unit whichUnit, integer newUnitId, integer unitStateMethod returns unit local unit oldUnit = whichUnit local unit newUnit local boolean wasHidden local integer index local item indexItem local real oldRatio // If we have bogus data, don't attempt the replace. if (oldUnit == null) then set bj_lastReplacedUnit = oldUnit return oldUnit endif // Hide the original unit. set wasHidden = IsUnitHidden(oldUnit) call ShowUnit(oldUnit, false) // Create the replacement unit. if (newUnitId == 'ugol') then set newUnit = CreateBlightedGoldmine(GetOwningPlayer(oldUnit), GetUnitX(oldUnit), GetUnitY(oldUnit), GetUnitFacing(oldUnit)) else set newUnit = CreateUnit(GetOwningPlayer(oldUnit), newUnitId, GetUnitX(oldUnit), GetUnitY(oldUnit), GetUnitFacing(oldUnit)) endif // Set the unit's life and mana according to the requested method. if (unitStateMethod == bj_UNIT_STATE_METHOD_RELATIVE) then // Set the replacement's current/max life ratio to that of the old unit. // If both units have mana, do the same for mana. if (GetUnitState(oldUnit, UNIT_STATE_MAX_LIFE) > 0) then set oldRatio = GetUnitState(oldUnit, UNIT_STATE_LIFE) / GetUnitState(oldUnit, UNIT_STATE_MAX_LIFE) call SetUnitState(newUnit, UNIT_STATE_LIFE, oldRatio * GetUnitState(newUnit, UNIT_STATE_MAX_LIFE)) endif if (GetUnitState(oldUnit, UNIT_STATE_MAX_MANA) > 0) and (GetUnitState(newUnit, UNIT_STATE_MAX_MANA) > 0) then set oldRatio = GetUnitState(oldUnit, UNIT_STATE_MANA) / GetUnitState(oldUnit, UNIT_STATE_MAX_MANA) call SetUnitState(newUnit, UNIT_STATE_MANA, oldRatio * GetUnitState(newUnit, UNIT_STATE_MAX_MANA)) endif elseif (unitStateMethod == bj_UNIT_STATE_METHOD_ABSOLUTE) then // Set the replacement's current life to that of the old unit. // If the new unit has mana, do the same for mana. call SetUnitState(newUnit, UNIT_STATE_LIFE, GetUnitState(oldUnit, UNIT_STATE_LIFE)) if (GetUnitState(newUnit, UNIT_STATE_MAX_MANA) > 0) then call SetUnitState(newUnit, UNIT_STATE_MANA, GetUnitState(oldUnit, UNIT_STATE_MANA)) endif elseif (unitStateMethod == bj_UNIT_STATE_METHOD_DEFAULTS) then // The newly created unit should already have default life and mana. elseif (unitStateMethod == bj_UNIT_STATE_METHOD_MAXIMUM) then // Use max life and mana. call SetUnitState(newUnit, UNIT_STATE_LIFE, GetUnitState(newUnit, UNIT_STATE_MAX_LIFE)) call SetUnitState(newUnit, UNIT_STATE_MANA, GetUnitState(newUnit, UNIT_STATE_MAX_MANA)) else // Unrecognized unit state method - ignore the request. endif // Mirror properties of the old unit onto the new unit. //call PauseUnit(newUnit, IsUnitPaused(oldUnit)) call SetResourceAmount(newUnit, GetResourceAmount(oldUnit)) // If both the old and new units are heroes, handle their hero info. if (IsUnitType(oldUnit, UNIT_TYPE_HERO) and IsUnitType(newUnit, UNIT_TYPE_HERO)) then call SetHeroXP(newUnit, GetHeroXP(oldUnit), false) set index = 0 loop set indexItem = UnitItemInSlot(oldUnit, index) if (indexItem != null) then call UnitRemoveItem(oldUnit, indexItem) call UnitAddItem(newUnit, indexItem) endif set index = index + 1 exitwhen index >= bj_MAX_INVENTORY endloop endif // Remove or kill the original unit. It is sometimes unsafe to remove // hidden units, so kill the original unit if it was previously hidden. if wasHidden then call KillUnit(oldUnit) call RemoveUnit(oldUnit) else call RemoveUnit(oldUnit) endif set bj_lastReplacedUnit = newUnit return newUnit endfunction As you can see in this function variables like oldUnit and indexItem are never set to null, not to mention newUnit which can't be set to null because its the return value and as far as i know a function can't take any actions after returning (like in C) so i would like to know why ppl says that not nullying variables (locals, which are suposed to be dealocated at the end of a function) can cause a memory leak. In the case it does well all the functions in blizzard.j that uses locals that are handle or child of handle LEAK because i didnt see even one where the locals are set to null. I undestand the reason why an object needs to be destroyed (deallocated) but theres no reason for nulling a pointer that is a local in a function, because the local is supposed to be auto-dealocated after the funcion ends. Please someone experienced answer this question. Thanks! EDIT: I realy dont find it logic, and it makes making clean maps imposible, because blizzard.j functions are in every map |
| 05-02-2006, 09:41 PM | #9 | |
Yes, many functions in Blizzard.j will take bits of memory hostage. This is one reason we try to avoid Blizzard.j, however, I believe Vexorian's map optimizer fixes these leaks automagically, if your map is compatible with the appropriate option. Quote:
There are ways to work around the bug when you need to return the variable. http://www.wc3campaigns.net/showthread.php?t=81872 |
| 05-02-2006, 09:43 PM | #10 |
The game does have a garbage collector, it always cleans stuff once the map ends or you exit the game. It is not that hard to avoid leaks, with some it is impossible(in JASS) but the ones that are not manually cleanable don't matter than much. And it is not difficult to beat leaks in C++ either |
| 05-02-2006, 09:57 PM | #11 |
Thanks ppl, ill try not to use blizzard.j functions then, anyways as im new to JASS programming and in the first place i wanted to learn it cuz my map has a lot of gui triggers and i wanted to give players with power less comps a chance to reach to the end of the game without droping. So in fact what im doing is converting my gui triggers to jass and using locals to fix leaks, but then i found that most of the functions that i use come with leaks which is realy frustrating cuz i dont have time to make them from zero, so ill just do what i can, thanks for the help ppl. EDIT: you mentioned that vex optimizer can fix blizzard.j bugs if i select the right option, can you tell me whats the option plz? Im actualy using the optimizer and i use both map optimization options, and script optimization methods checked By he way, excelent tool man! |
| 05-04-2006, 03:51 AM | #12 |
There's an option that says Anti BJ. Optimizer won't fix your all your leaks though. You'll have to continue converting your stuff to JASS and fix it manually. Agreed though, it is an awesome tool. |
| 05-04-2006, 03:57 AM | #13 |
It will only fix the blizzard.j leaks caused by variables not being set to null. The rest is for you to fix |
| 05-05-2006, 06:56 PM | #14 |
what about the locals that come with the function declaration, i mean the parameters for example if i have: JASS:function Example takes player p returns nothing //do some stuff endfunction does the local var "p" leaks? this depends on which the paramter is given to the function by reference or by value. EDIT: can destroy Start trigger after its execution? in case i can, can it be at the end of the trigger: JASS:function Init takes nothing returns nothing //do some stuff call DestroyTrigger(gg_trg_Init) endfunction |
| 05-05-2006, 09:42 PM | #15 |
The only way to get a variable to take a handle hostage is with the set statement. Using a function argument, blessedly, will not incur leaks. However, player variables will never result in a leak since you never deallocate them. You don't lose a little chunk each time you forget to change the variable before the function returns, you lose a little chunk if you once forgot to change when you deallocate the object. You can destroy any trigger. Not everything is run by triggers. The engine calls the main function when the script begins. If you tried to destroy GetTriggeringTrigger there.. Probably nothing would happen. Maybe it is run by a trigger, and you can reset the map by running it. Edit: Oh, all arguments are passed by value. But I don't see how that's relevant. BTW, the only variable type is 4 byte long. |
