Is it possible to disable the garbage collector?

Started by
6 comments, last by vdaras 12 years, 9 months ago
Hello,

I need to disable angelscript's garbage collector. The lifetime of the objects that the scripts manipulate is managed by C++ code, thus my only need is to pass non-refcounted handles to the scripts.

Also, if I manage to disable garbage collection, is there a way to prevent the instantiation of reference types in scripts in order to avoid memory leaks?

Thank you in advance.
Advertisement
Application registered types that have been registered without the asOBJ_GC flag won't be garbage collected.

You still need to register the ADDREF and RELEASE behaviours for reference types, as it is a basic requirement by the engine. But if you really don't care about the reference counting you can just register them with dummy functions that don't do anything.


void dummy(void*) { }

engine->RegisterObjectType("reftype", 0, asOBJ_REF);
engine->RegisterObjectBehaviour("reftype", asBEHAVE_ADDREF, "void f()", asFUNCTION(dummy), asCALL_CDECL_OBJLAST);
engine->RegisterObjectBehaviour("reftype", asBEHAVE_RELEASE, "void f()", asFUNCTION(dummy), asCALL_CDECL_OBJLAST);


If you want to prevent the scripts from creating new instances of these types, simply don't register the factory behaviour.

Disabling the garbage collector is not possible, as it is used for internal objects as well as for script classes. However, if you do not wish to run the GC automatically you can turn off that with a call to engine->SetEngineProperty(asEP_AUTO_GARBAGE_COLLECT, false);

Regards,
Andreas

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

Thank you for your fast response Andreas, you really helped a lot smile.gif


Hello,

Got another question regarding memory management. I explicitly create asIScriptOjbect s in C++ code, which are only manipulated by the C++ engine and angelscript code doesn't use them. When I try to deallocate them by calling asIScriptObject::Release I see that there are 3 references to each one of them and I call this method in a do-while loop until the object's references reach 0. Will these objects be deallocated by the garbage collector? If not, what can I do to deallocate them explicitly? I don't want to mess with angelscript's code and mark asIScriptObject destructor as public, I don't think it would be safe either.

Regards,
Vassilis
You must only call Release() for the references you own. From what you're saying you're doing the following:


asIScriptObject *obj = something();
while( obj->Release() > 0 );


I hope that is not the case. While it may have worked in some cases, it will inevitably crash the application at one point or another.

A script object may be garbage collected, even though you create it yourself outside of the scripts. This depends on the members of the class. If for example the class has a handle as member, it will be garbage collected as there is no way of predicting at compile time whether the object will ever be involved in a circular reference or not.

If you want to be sure a script object is actually destroyed, you should call Release() for each reference you own. If the last call to Release() still indicate more references, then call engine->GarbageCollect(asGC_FULL_CYCLE); to make sure the garbage collector frees it's own references to the object.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game


You must only call Release() for the references you own. From what you're saying you're doing the following:


asIScriptObject *obj = something();
while( obj->Release() > 0 );


I hope that is not the case. While it may have worked in some cases, it will inevitably crash the application at one point or another.




Unfortunately that is the case, thanks for the clarification.



A script object may be garbage collected, even though you create it yourself outside of the scripts. This depends on the members of the class. If for example the class has a handle as member, it will be garbage collected as there is no way of predicting at compile time whether the object will ever be involved in a circular reference or not.

If you want to be sure a script object is actually destroyed, you should call Release() for each reference you own. If the last call to Release() still indicate more references, then call engine->GarbageCollect(asGC_FULL_CYCLE); to make sure the garbage collector frees it's own references to the object.



The asIScriptObject I create is wrapped inside a class that handles its lifetime and executing its methods.

Here is the class' interface



class ScriptLogic : public AbstractLogic, public NonCopyable
{
private:
asIScriptObject *m_script;
asIObjectType *m_scriptObjectType;
asIScriptContext *m_ctx;

public:

/**
* Constrcutor initializes this logic by getting the script object
* that handles the behavior of this Entity.
*
* @param object - the script object managed by the script logic.
*/

ScriptLogic(asIScriptObject *object);


virtual ~ScriptLogic();


/**
* Clones this script logic.
*
* @return a replicate of ths Script Logic component. (deep copy)
*/

ScriptLogic* Clone();


/**
* Executes the Initialize method of the script object.
*/

void ExecuteInitialize();


/**
* Executes the Update method of the script object.
*/

void ExecuteUpdate();

/**
* Executes the Update method of the script object.
*/

void ExecuteOnTileCollision(std::string* tile, Math::Vector2F& contactNormal);


/**
* Executes the Update method of the script object.
*/

void ExecuteOnEntityCollision(Entity* collided, Math::Vector2F& contactNormal);

/**
* Executes the OnKeyButtonUp method of the script object.
*
* @param info for the released key
*/

void ExecuteOnKeyButtonUp(KeyboardEventInfo *info);


/**
* Executes the OnKeyButtonDown method of the script object.
*
* @param info for the pressed key
*/

void ExecuteOnKeyButtonDown(KeyboardEventInfo *info);


/**
* Execute the OnDeath method of the script object.
*/

void ExecuteOnDeath();


/**
* Sets the Entity that this script logic controls.
*
* @param parent
*/

virtual void SetParent(Entity *parent);

/**
* Returns the name of this script's class.
*/

const char *getName();
};


Here is the constructor's, the destructor's, Clone method and a sample script object's method execution implementations.



ScriptLogic::ScriptLogic(asIScriptObject *object)
{
ScriptManager *scriptManager = ScriptManager::GetInstance();

//get the type of the script
m_scriptObjectType = object->GetObjectType();
//create an object of the given type
m_script = object;
//give a context to this script logic
m_ctx = scriptManager->CreateContext();
}



ScriptLogic::~ScriptLogic()
{
m_script->Release();
m_scriptObjectType->Release();
m_ctx->Release();
}


ScriptLogic* ScriptLogic::Clone()
{
ScriptManager *scriptManager = ScriptManager::GetInstance();
asIScriptEngine *engine = scriptManager->GetEngine();

//create a copy of the script object
asIScriptObject *replicate = (asIScriptObject *) engine->CreateScriptObjectCopy(m_script, m_scriptObjectType->GetTypeId());

return new ScriptLogic(replicate);
}


void ScriptLogic::ExecuteUpdate()
{
//get the method 'Update' ID
int methodID = m_scriptObjectType->GetMethodIdByDecl("void Update()", true);
//prepare the method for execution
m_ctx->Prepare(methodID);
//pass the script logic's script object as the object which calls the method
m_ctx->SetObject(m_script);
//execute the method
m_ctx->Execute();
}


Here is the creation of the asIScriptObject object.




//create the object
int typeID = entityModule->GetTypeIdByDecl(scriptObjectType.c_str());
asIScriptObject *scriptObject = (asIScriptObject *) engine->CreateScriptObject(typeID);

[irrelevant code omitted]

//add the script object to the prototype entity

cacheEntity->AddScriptLogic(scriptObject); //this creates a ScriptLogic object, with the asIScriptObject as an argument



The asIScriptObject objects in ScriptLogic object are not accessed by any other part of the engine's code. Based on this code, I can only count 1 reference added during creation and 1 reference held by the garbage collector. So.. there is extra reference that I can't justify. sad.gif
As you execute the script the context holds on to a reference, until the context is released or you prepare it for a new execution. That is likely the 3rd reference you're seeing.


[color="#000000"]Irrelevant to the reference counting question, may I suggest you remove the context as a member of the ScriptLogic class? Instead keep a single context in the [color="#660066"][color="#000000"]ScriptManager class.

Since all your scripts executes and returns immediately, there is no need for multiple contexts. It will just waste a lot of memory as each context keep quite a bit of memory to hold local variables on the call stack for an execution.

[color="#000000"]Also, you should cache the function id's so you do not have to call the GetMethodIdByDecl() method for every execution. This is a quite slow method, as it needs to parse the declaration and then compare it against all of the compiled methods in order to determine which you want.



Regards,
Andreas

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game


As you execute the script the context holds on to a reference, until the context is released or you prepare it for a new execution. That is likely the 3rd reference you're seeing.



Aha, ok, I'll try releasing the context before the script object and see if the reference count drops by one. Thanks :)


[color="#000000"]Irrelevant to the reference counting question, may I suggest you remove the context as a member of the ScriptLogic class? Instead keep a single context in the [color="#660066"][color="#000000"]ScriptManager class.


Since all your scripts executes and returns immediately, there is no need for multiple contexts. It will just waste a lot of memory as each context keep quite a bit of memory to hold local variables on the call stack for an execution.

[color="#660066"]
[color="#660066"][color="#000000"]It was design choice, we thought that having multiple contexts would later allow us to integrate concurrent script execution more easily. As for now, it's indeed a waste of space to have a context for each script object.
[color="#660066"]
[color="#660066"][color="#000000"]


[color="#000000"]Also, you should cache the function id's so you do not have to call the GetMethodIdByDecl() method for every execution. This is a quite slow method, as it needs to parse the declaration and then compare it against all of the compiled methods in order to determine which you want.




Regards,
Andreas



Thanks for the tip. biggrin.gif

Regards,
Vassilis


This topic is closed to new replies.

Advertisement