Scripting: Automatic Reference Counting – Arma Reforger

From Bohemia Interactive Community
Jump to navigation Jump to search
m (1 revision imported)
m (→‎Strong Reference: Fix wording)
 
(8 intermediate revisions by the same user not shown)
Line 1: Line 1:
{{TOC|side}}
'''Automatic Reference Counting''', shortened to '''ARC''', is a memory management solution used in Enforce Script. Enforce Script does not use Garbage Collection (a.k.a GC).
'''Automatic Reference Counting''', shortened to '''ARC''', is a memory management solution used in Enforce Script. Enforce Script does not use Garbage Collection (a.k.a GC).
<!--
<!--
Line 16: Line 17:
* suitable to use by everyone
* suitable to use by everyone
-->
-->
In comparison with the widely used {{Wikipedia|Tracing garbage collection}} (in e.g C#, Java):
In comparison with the widely used {{Link|https://en.wikipedia.org/wiki/Tracing_garbage_collection}} (in e.g C#, Java):
* it does not suffer from cleanup stuttering (lags due to high GC load)
* it does not suffer from cleanup stuttering (lags due to high GC load)
* memory management performance is not dependent on the number of managed instances
* memory management performance is not dependent on the number of managed instances
* it is simple to implement (having a good working GC is a big issue)
* it is simple to implement (having a good working GC is a big issue)


on the negative side, it is open to the cyclic reference problem, described in the {{HashLink|#Cyclic Reference}} paragraph below.
On the negative side, it is open to the cyclic reference problem, described in the {{Link|#Cyclic Reference}} paragraph below.


{{Feature|informative|This whole system impacts '''objects''' passed by '''reference''' - see [[Arma_Reforger:Scripting:_Values#Types|Types]].}}
{{Feature|informative|This whole system impacts '''objects''' passed by '''reference''' - see {{Link|Arma Reforger:Scripting: Values#Types|Value Types}}.}}




Line 32: Line 33:
=== Strong Reference ===
=== Strong Reference ===


A strong reference is a reference to an object that increments the reference counter. The referenced object '''cannot''' become null during the program's lifetime unless manually deleted (with the {{hl|[[Arma Reforger:Scripting: Keywords#delete|delete]]}} keyword).
A strong reference is a reference to an object that increments the reference counter.
The referenced object '''cannot''' become null during the referencing object's lifetime unless manually deleted (with the {{hl|[[Arma Reforger:Scripting: Keywords#delete|delete]]}} keyword).


Strong references:
Strong references:
Line 44: Line 46:
== Usage ==
== Usage ==


<syntaxhighlight lang="cpp">
<enforce>
class ExampleClass
class ExampleClass
{
{
Line 85: Line 87:
// arrayOfWeakRef is destroyed (it only contains NULL),
// arrayOfWeakRef is destroyed (it only contains NULL),
// arrayOfStrongRef is destroyed (it contains the last strong ref to object, so its contained object is destroyed as well
// arrayOfStrongRef is destroyed (it contains the last strong ref to object, so its contained object is destroyed as well
};
}
};
}
</syntaxhighlight>
</enforce>




Line 103: Line 105:
** they remain in memory until the program is stopped
** they remain in memory until the program is stopped


<syntaxhighlight lang="cpp">
<enforce>
class Parent
class Parent
{
{
ref Child m_child;
ref Child m_Child;
};
}


class Child
class Child
{
{
ref Parent m_parent;
ref Parent m_Parent;
};
}


void main()
void main()
Line 118: Line 120:
Parent a = new Parent(); // 'a' has 1 reference
Parent a = new Parent(); // 'a' has 1 reference
Child b = new Child(); // 'b' has 1 reference
Child b = new Child(); // 'b' has 1 reference
a.m_child = b; // 'b' has 2 references
a.m_Child = b; // 'b' has 2 references
b.m_parent = a; // 'a' has 2 references
b.m_Parent = a; // 'a' has 2 references


// local variables 'a', 'b' are released (reference count is decreased by 1)
// local variables 'a', 'b' are released (reference count is decreased by 1)
// both objects remain in memory, having 1 reference each (each other)
// both objects remain in memory, having 1 reference each (each other)
};
}
</syntaxhighlight>
</enforce>


==== Solution ====
==== Solution ====
The solution to this issue is to have one object have a strong reference, while the other only holds a weak one:
The solution to this issue is to have one object have a strong reference, while the other only holds a weak one:
<syntaxhighlight lang="cpp">
<enforce>
class Parent
class Parent
{
{
ref Child m_child;
ref Child m_Child;
};
}


class Child
class Child
{
{
Parent m_parent;
Parent m_Parent;
};
}


void main()
void main()
Line 143: Line 145:
Parent a = new Parent(); // 'a' has 1 strong (scope) reference
Parent a = new Parent(); // 'a' has 1 strong (scope) reference
Child b = new Child(); // 'b' has 1 strong (scope) reference
Child b = new Child(); // 'b' has 1 strong (scope) reference
a.m_child = b; // 'b' has 2 strong references
a.m_Child = b; // 'b' has 2 strong references
b.m_parent = a; // 'a' has 1 strong reference, 1 weak reference
b.m_Parent = a; // 'a' has 1 strong reference, 1 weak reference


// local variables 'a', 'b' are released (reference count is decreased by 1)
// local variables 'a', 'b' are released (reference count is decreased by 1)
// a only has 1 weak reference and is released from memory
// a only has 1 weak reference and is released from memory
// b loses a's reference and has no more reference to it - it gets released
// b loses a's reference and has no more reference to it - it gets released
};
}
</syntaxhighlight>
</enforce>
Using weak references makes null-checking take more importance in scripting.
Using weak references makes null-checking take more importance in scripting.




{{GameCategory|armaR|Modding|Guidelines|Scripting}}
{{GameCategory|armaR|Modding|Guidelines|Scripting}}

Latest revision as of 12:11, 26 April 2024

Automatic Reference Counting, shortened to ARC, is a memory management solution used in Enforce Script. Enforce Script does not use Garbage Collection (a.k.a GC). In comparison with the widely used Tracing garbage collection (in e.g C#, Java):

  • it does not suffer from cleanup stuttering (lags due to high GC load)
  • memory management performance is not dependent on the number of managed instances
  • it is simple to implement (having a good working GC is a big issue)

On the negative side, it is open to the cyclic reference problem, described in the Cyclic Reference paragraph below.

This whole system impacts objects passed by reference - see Value Types.


Principle

Enfusion has an internal counter of strong references to objects; when this counter reaches 0, the object is released from memory.

Strong Reference

A strong reference is a reference to an object that increments the reference counter. The referenced object cannot become null during the referencing object's lifetime unless manually deleted (with the delete keyword).

Strong references:

  • using the ref keyword
  • scope lifetime's script reference

Weak Reference

A weak reference is a reference to an object that does not increment the reference counter. The reference may become null during the program's lifetime.

Usage

class ExampleClass { // weak reference to an object ReferencedClass m_WeakRefObject; // strong reference to an object ref ReferencedClass m_StrongRefObject; // strong reference to a dynamic array (that is an object itself) of weak references ref array<ReferencedClass> m_aWeakRefArray; // strong reference to dynamic array of strong references ref array<ref ReferencedClass> m_aStrongRefArray; // static array of strong references ref ReferencedClass m_aStrongRefStaticArray[10]; // maps and sets work the same ref map<string, ReferencedClass> m_mWeakRefMap; ref map<string, ref ReferencedClass> m_mStrongRefMap; ref set<ReferencedClass> m_sWeakRefSet; ref set<ref ReferencedClass> m_sStrongRefSet; void Method() { ReferencedClass localReference = new ReferencedClass(); localReference = new ReferencedClass(); // the previous object loses its strong reference and is immediately released // the newly created object replaces it array<ReferencedClass> arrayOfWeakRef = new array<ReferencedClass>(); // array of weak references is created arrayOfWeakRef.Insert(new ReferencedClass()); // ReferencedClass object is created and immediately deleted, as arrayOfWeakRef does not increment the reference counter array<ref ReferencedClass> arrayOfStrongRef = new array<ref ReferencedClass>(); // array of strong references is created arrayOfStrongRef.Insert(new ReferencedClass()); // ReferencedClass object is created and kept // at the end of the method, local variables 'localReference', 'arrayOfWeakRef' and 'arrayOfStrongRef' are released: // localReference is destroyed // arrayOfWeakRef is destroyed (it only contains NULL), // arrayOfStrongRef is destroyed (it contains the last strong ref to object, so its contained object is destroyed as well } }


Cyclic Reference

Problem

The main issue with reference counting is cyclic references (aka "Island of isolation"). In the code below:

  • the main method creates object A and object B
  • object A references object B
  • object B references object A
  • the main method ends, dropping its references to object A and B
  • both objects a and b should be deleted but they still reference each other, keeping the reference counting above 0:
    • any reference to them is lost after main method's ending
    • they remain in memory until the program is stopped

class Parent { ref Child m_Child; } class Child { ref Parent m_Parent; } void main() { Parent a = new Parent(); // 'a' has 1 reference Child b = new Child(); // 'b' has 1 reference a.m_Child = b; // 'b' has 2 references b.m_Parent = a; // 'a' has 2 references // local variables 'a', 'b' are released (reference count is decreased by 1) // both objects remain in memory, having 1 reference each (each other) }

Solution

The solution to this issue is to have one object have a strong reference, while the other only holds a weak one:

class Parent { ref Child m_Child; } class Child { Parent m_Parent; } void main() { Parent a = new Parent(); // 'a' has 1 strong (scope) reference Child b = new Child(); // 'b' has 1 strong (scope) reference a.m_Child = b; // 'b' has 2 strong references b.m_Parent = a; // 'a' has 1 strong reference, 1 weak reference // local variables 'a', 'b' are released (reference count is decreased by 1) // a only has 1 weak reference and is released from memory // b loses a's reference and has no more reference to it - it gets released }

Using weak references makes null-checking take more importance in scripting.