Links

Categories

Tags


« | Main | »

I get weak in the presence of weak-references

By Jewe | December 22, 2014

Despite the humorous title, this post’s topic is weak references – and it will be rather technical. JewelScript users who want a better understanding of the inner workings of the runtime can learn here, what weak references are, why they can be dangerous, why we need them, and how they should be used.

1. Reference counting is not scary

Many developers shy away from reference counted virtual machines, because they are afraid that it’ll make their lives hard and coding ugly. They worry they need to properly add-ref and release every single little object, and that ending up with a huge memory leak is inevitable.

I know, because I worried about that too. That’s why I tried to make the JewelScript API as painless as possible. And in fact, I think the details of reference counting are well hidden away and hardly bothersome at all. You almost never add-ref anything, and releasing unneeded objects should come as natural as using delete for a C++ or free() for a C developer.

The programs released on this site are testimony to my claim that JewelScript’s reference counting is no sorcery. They will run for weeks without any significant increase in memory usage. And I’m no sorcerer – just a plain old programmer guy.

However, there is one serious problem all reference counted systems suffer from, and so does the JewelScript virtual machine: The reference cycle problem.

2. Reference cycles – ’cause shit happens

Reference counting actually becomes useless when an object directly or indirectly references itself. If A directly references itself, then A has a constant reference count of 1 – and thus cannot be freed.

For the same reason, if A directly references B, and B references A, then A indirectly references itself – and again cannot be freed. Here’s a code example for that:

class Object
{
    method Object()
    {
        this.ref = null;
    }
    Object ref;
}

function main()
{
    // create objects
    Object A;
    Object B;

    // have them reference each other (this creates a reference cycle)
    A.ref = B;
    B.ref = A;
}

In real life, reference cycles can be much more complex than this simple A/B example. Imagine a large tree structure of objects where all child objects reference the root of the tree. This would create a huge memory leak.

This problem is the reason why virtual machines based on reference counting come with an additional garbage collection mechanism. JewelScript offers an optional mark-and-sweep garbage collector to search the heap for reference cycles and free them.

However, this is like constantly cleaning up behind someone who spills dirt everywhere they walk. It also puts a considerable strain on memory and CPU usage. A better strategy would be to avoid the creation of reference cycles in the first place. And this is where weak references come in.

3. Weak references are evil

We all know from watching horror movies that sometimes, evil can only be purged by unleashing even greater evil. Fight fire with fire, so to speak. In the JewelScript virtual machine, this greater evil is represented by weak references.

In essence, a weak reference is a reference that isn’t counted. Because of this, they allow us to break reference cycles. If only one reference in a cycle is weak, then the cycle can be automatically freed by the virtual machine. Hence, there won’t be any leaked objects and no need for a garbage collector.

Sounds good? Well here’s the catch: Normal references are always safe, because the virtual machine can guarantee that an object referenced by a variable always exists. An object can’t be destroyed if there are still references to it. Weak references don’t provide that kind of safety. Because their reference isn’t counted, an object might have been destroyed even though there are still weak references to it. In addition to that, a weak reference can actually produce a memory leak, if no other counted reference exists for the value.

In the native C / C++ world, weak references are often required, because there are cases where developers cannot allow the JewelScript runtime to manage the life time of certain objects. This is basically true for all kinds of objects that the application does not ‘own’ and isn’t allowed to destroy. System resources like device handles, draw contexts and the likes are examples for these kinds of objects.

Would we create normal references for these objects, then the virtual machine would try to manage their life time, and consequently try to destroy them once their reference count reaches zero. So in the native C / C++ world, weak references are actually important and not evil at all, as they allow the application to maintain control over the life time of certain objects.

It’s the script code where weak references become evil, especially if used by someone who doesn’t know what they’re doing. However, as our Object example above demonstrated, there’s good reason to support weak references in script code as well.

4. Making variables weak

To prevent script code from creating reference cycles, JewelScript supports the modifier keyword weak in any variable declaration. This will instruct the compiler to make the variable a weak reference variable:

class Object
{
    method Object()
    {
        this.ref = null;
    }
    weak Object ref; // make 'ref' a weak reference
}

function main()
{
    // create objects
    Object A;
    Object B;

    // have them reference each other
    A.ref = B;
    B.ref = A;
}

This will fix the reference cycle created in function main() and we will have no memory leak. However, it also makes class Object unsafe, because variable ‘ref’ is now always a weak reference, regardless how the class is being used in other functions. Consider this function:

function foo()
{
    // create objects
    Object A, B, C, D;

    // have them reference each other
    A.ref = B;
    B.ref = C;
    C.ref = D;
}

Function foo() doesn’t create a reference cycle. No object directly or indirectly references itself. But nonetheless all of them use weak references. This can create a lot of problems in the long run, especially if the code gets more complex. It may even cause memory leaks or crashes.

Generally making a variable a weak reference is not a good option, as it actually depends on the value to assign whether we expect a reference cycle or not. Therefore JewelScript introduced the weak cast operator in the latest version of the language.

5. Saved by the weak cast operator

As we observed in the previous section, when it comes to assigning references, whether we expect to create a reference cycle often depends on the value to assign, not the variable. Because of this, the weak cast operator was introduced in the latest version of JewelScript. This operator allows us to create a weak reference from any value, and then assign it to any variable:

class Object
{
    method Object()
    {
        this.ref = null;
    }
    Object ref;
}

function main()
{
    // create objects
    Object A;
    Object B;

    A.ref = B;          // assign normal reference to B
    B.ref = (weak)A;    // assign weak reference to A
}

Using the weak cast operator, we can create a weak reference from any value when needed – and without having to code it statically into a class. In almost all cases where weak references are needed, this should suffice.

Only in very rare cases should it be necessary to declare a variable as weak – namely when the variable’s actual purpose is to directly or indirectly reference the object it belongs to.

To learn more about weak references, see the JewelScript language reference.

Topics: docs, news | Comments Off on I get weak in the presence of weak-references

Comments are closed.