Movatterモバイル変換


[0]ホーム

URL:


This site has been retired. For up to date information, seehandbook.gnome.org orgitlab.gnome.org.


[Home] [TitleIndex] [WordIndex

Contents

  1. Vala's Memory Management Explained
    1. Unowned References
  2. Memory Management by Example
    1. Normal, reference-counted classes
    2. Manual memory management with pointer syntax
    3. Compact classes without reference counting
    4. Compact classes with reference counting
    5. Immutable compact classes with a copy function

Vala's Memory Management Explained

Vala's memory management is based on automatic reference counting instead of tracing garbage collection.

This has bothadvantages and disadvantages. Reference counting is deterministic, but you can form reference cycles in some cases. In these cases you must useweak references in order to break those cycles. The Vala keyword for this isweak.

This is how automatic reference counting works:

Each time a reference type object is assigned to a variable (referenced) its internal reference count is increased by one (ref), each time a reference variable goes out of scope the object's internal reference count is decreased by one (unref).

If an object's internal reference count reaches 0 the object getsfreed. When an object is freed each of its reference type data members' reference count is decreased by one (unref). If one of these reaches 0 it gets freed with the same procedure, and so on.

But what is a reference cycle and why is it bad? Let's have a look at a simpledoubly-linked list implementation without data:

classNode :Object {publicNodeprev;publicNodenext;publicNode (Node?prev =null) {this.prev =prev;// refif (prev !=null) {prev.next =this;// ref        }    }}voidmain () {varn1 =newNode ();// refvarn2 =newNode (n1);// ref// print the reference count of both objectsstdout.printf ("%u, %u\n",n1.ref_count,n2.ref_count);}// unref, unref

The places where referencing and unreferencing happens are commented for your better understanding in this example. The following figure illustrates the situation aftern1 andn2 are assigned:

refcycle1.png

Each arrow (edge) in the graph represents a reference and each node has a number indicating the object's reference count (the number of arrows pointing at it).null references are not shown. You can easily spot the reference cycle (a roundtrip by following the arrows). Aftern1 andn2 are unrefed at the end of the block both nodes will have a reference count of 1, but none of them will reach 0 and be freed.

In our case we're lucky, because the program terminates anyway and the operating system will free all memory of the process anyway. But what would happen if the program ran longer? For example:

voidmain () {while (true) {varn1 =newNode ();varn2 =newNode (n1);Thread.usleep (1000);    }}

Open a task manager / process monitor (e.g.gnome-system-monitor) and start the program. You can watch it eating up your memory. Break it before it slows down your system. (The memory will be freed immediately.)

An equivalent C# or Java program would have no problem, because a tracing garbage collector regularly throws away all objects that are no longer reachable, either directly or indirectly, from the current scope. But in Vala you have to take a countermeasure in the case of a reference cycle.

We can break the cycle by making one of the references in the cycle aweak reference:

publicweakNodeprev;publicNodenext;

This means assignment to such a variable won't increase the object's reference count. Now the situation looks like this:

refcycle2.png

The reference count of the first object is 1, not 2 as it was before. Whenn1 andn2 go out of scope at the end of the block both objects' reference counts are decreased by one. The first object is freed, because its reference count hits 0, the second one will have a reference count of 1. However, because the first object held a reference to the second object the reference count of the second object is decreased again, hits 0 and the second object gets freed as well.

Run the program again and you will see that the memory consumption stays stable.

A reference cycle does not necessarily have to be a direct cycle. This is a reference cycle as well:

refcycle3.png

Unowned References

All objects of Vala classes and most objects of gobject-based libraries are reference counted. However, Vala allows you to use classes of non-gobject-based C libraries that have no reference counting support by default, too. These classes are calledcompact classes (annotated with the[Compact] attribute).

Non reference counted objects may have only one strong reference (think of it as the "owning" reference). When this reference goes out of scope the object is freed. All other references must beunowned references. When these references go out of scope the object will not be freed.

unowned-reference.png

So when you call a method (most likely of a non-gobject library) that returns an unowned reference (this is marked on the method's return type with theunowned keyword) to an object that you are interested in, then you have two choices: either copy the object if it has a copy method, then you can haveyour own single strong reference to the new copied object, or assign the reference to a variable that is declared with theunowned keyword and Vala will know that it should not free the object on your side.

Vala prevents you from assigning an unowned reference to a strong (i.e. not unowned) reference. However, you can transfer the ownership to another reference with(owned):

[Compact]classFoo { }voidmain () {Fooa =newFoo ();unownedFoob =a;Fooc = (owned)a;// 'c' is now the new "owning" reference}

ownership-transfer.png

By the way, Vala strings are not reference counted, too, because they are based on the C typechar*. However, Vala takes care of automatically copying them when needed. So you actually don't have to think about them. Only writers of bindings have to take care of whether a string reference is unowned or not and mark it in the API as appropriate.

Currently many types in the Vala binding APIs are falsely markedweak even though they should actually beunowned. This is because in the past there was only theweak keyword for both, so don't let this confuse you. At the momentweak andunowned can be used interchangeably. However, you should useweak only for breaking reference cycles andunowned only for ownership issues as described obove.

Memory Management by Example

Normal, reference-counted classes

publicclassFoo {publicvoidmethod () { }}voidmain () {Foofoo =newFoo ();// allocate, reffoo.method ();Foobar =foo;// ref}// unref, unref => free

Everything is managed automatically.

Manual memory management with pointer syntax

You can always choose to do manual memory management if you feel as if you musthave full control. The pointer syntax is similar to C/C++:

voidmain () {Foo*foo =newFoo ();// allocatefoo->method ();Foo*bar =foo;deletefoo;// free}

Compact classes without reference counting

Compact classes are classes that are not registered with Vala's type system.They are usually classes bound from non gobject-based C libraries. However,you can also define your own compact classes in Vala if you can live withthe fact that they are very limited feature-wise (e.g. no inheritance, nointerface implementation, no private fields, etc.).

Creating and destroying compact classes is faster than non-compact classes(about 2.5 times faster than regular classes, and an order of magnitudefaster than GObject-derived classes), though other operations are nodifferent. That said, modern hardware can create and destroy millions ofGObject-derived classes per thread per second, so it is advisable to makesure this is a performance bottleneck before trying to optimize it away.

Compact classes do not support reference counting by default. So there canbe only one "owning" reference that will cause the object to be freed whenit goes out of scope, all other references have to be unowned.

[Compact]publicclassFoo {publicvoidmethod ();}voidmain () {Foofoo =newFoo ();// allocatefoo.method ();unownedFoobar =foo;Foobaz = (owned)foo;/* ownership transfer: now 'baz' is the "owning"                                reference for the object */unownedFoobam =baz;}// free ("owning" reference 'baz' went out of scope)

Compact classes with reference counting

You can add reference counting to a compact class by implementing it manually.You have to tell Vala which functions it should use for referencing andunreferencing with aCCode attribute.

[Compact][CCode (ref_function ="foo_up",unref_function ="foo_down")]publicclassFoo {publicintref_count =1;publicunownedFooup () {GLib.AtomicInt.add (refthis.ref_count,1);returnthis;    }publicvoiddown () {if (GLib.AtomicInt.dec_and_test (refthis.ref_count)) {this.free ();        }    }privateexternvoidfree ();publicvoidmethod () { }}voidmain () {Foofoo =newFoo ();// allocate, reffoo.method ();Foobar =foo;// ref}// unref, unref => free

As you can see, everything is managed automatically again.

Immutable compact classes with a copy function

If a compact class does not have reference counting support but is immutable(its internal state doesn't change) and has a copy function that duplicates the objectthen Vala will copy it automatically if it's assigned to a strong reference.

[Compact][Immutable][CCode (copy_function ="foo_copy")]publicclassFoo {publicvoidmethod () { }publicFoocopy () {returnnewFoo ();    }}voidmain () {Foofoo =newFoo ();// allocatefoo.method ();Foobar =foo;// copy}// free, free

If you want to prevent copying for fine tuning reasons you can still use an unowned reference:

voidmain () {Foofoo =newFoo ();// allocatefoo.method ();unownedFoobar =foo;}// free

2024-10-23 11:37
[8]ページ先頭

©2009-2025 Movatter.jp