Emojicode Documentation 1.0 beta 2

Memory Management

Hereโ€™s the good news about memory management in Emojicode: Emojicode does the hard work. This chapter describes the mechanisms employed.

Lifetime of Temporary Values

Emojicode manages memory with reference counting. This means that Emojicode maintains a count of how many references exist to every object. An object is destroyed immediately once there are no more references to it.

Consider the following example:

๐Ÿ†•๐ŸŸ  ๐Ÿ”คShawn๐Ÿ”คโ—๏ธ

The above code sample creates an instance of the class ๐ŸŸ. Since this object is not assigned to any variable or returned, we call it a temporary value. Temporary values are destroyed at the end of the statement in the order they were created.

Letโ€™s have a look at this more complicated example:

๐Ÿ‡ ๐Ÿฆ ๐Ÿ‡
  ๐Ÿ†• pet ๐ŸŸ ๐Ÿ‡๐Ÿ‰
๐Ÿ‰

๐Ÿ‡ ๐ŸŸ ๐Ÿ‡
  ๐Ÿ–๐Ÿ†• name ๐Ÿ”ก

  ๐Ÿ†• ๐Ÿผ name ๐Ÿ”ก ๐Ÿ‡๐Ÿ‰
๐Ÿ‰

๐Ÿ ๐Ÿ‡
  ๐Ÿ†•๐Ÿฆ ๐Ÿ†•๐ŸŸ ๐Ÿ”คShawn๐Ÿ”คโ—๏ธโ—
๐Ÿ‰

Here we create an instance of ๐Ÿฆ, which we pass an instance of ๐ŸŸ to. ๐Ÿฆ does not anything with the fish instance (like assigning it to an instance variable). So both the fish instance and the gorilla instance will be destroyed at the end of the statement. Because the fish instance was created first, it will be destroyed first.

Borrowing and Escaping Use

Emojicode supports the notion of borrowing and escaping use of a value.

This concept only applies to the use of method or initializer parameters and the use of the context, i.e. the value returned by ๐Ÿ‘‡, in the method or initializer.

A value is considered escaping, if it (or a copy of it) can outlive the call of the method or initializer. Consider, for instance, this class:

๐Ÿ‡ ๐Ÿฅง ๐Ÿ‡
  ๐Ÿ’ญ ...

  โ—๏ธ ๐Ÿ˜€ ๐Ÿ‡
    ๐Ÿ’ญ ...
  ๐Ÿ‰
๐Ÿ‰

๐Ÿ‡ ๐Ÿฆก ๐Ÿ‡
  ๐Ÿ–๐Ÿ†• pie ๐Ÿฅง

  ๐Ÿ†• ๐Ÿผ pie ๐Ÿฅง ๐Ÿ‡๐Ÿ‰
๐Ÿ‰

It is obvious that the pie reference passed to the ๐Ÿ†• initializer of ๐Ÿฆก will outlive the call as it is assigned to an instance variable. This parameter is considered escaping, therefore. On the other hand, the below class method does not use its parameter in an escaping way:

๐Ÿ‡ ๐ŸŸ ๐Ÿ‡
  ๐Ÿ‡โ—๏ธ ๐Ÿ’š pie ๐Ÿฅง ๐Ÿ‡
    ๐Ÿ˜€ pieโ—๏ธ
  ๐Ÿ‰
๐Ÿ‰

No copy of pie is made here that will outlive the call of ๐Ÿ’š.

As mentioned before, a method itself can be escaping, if it makes the this context outlive the call. The following is an example of such a method:

๐Ÿ‡ ๐Ÿฅง ๐Ÿ‡
  โ—๏ธ ๐Ÿ–ฒ โžก๏ธ ๐Ÿฆก ๐Ÿ‡
    โ†ฉ๏ธ ๐Ÿ†•๐Ÿฆก ๐Ÿ‘‡โ—๏ธ
  ๐Ÿ‰
๐Ÿ‰

๐Ÿ‘‡ is passed to an escaping parameter in this example, which obviously causes the value to escape.

Simply put, there are four ways in which a value can escape:

When compiling, the Emojicode compiler analyses all methods to determine whether they just borrow a value or let it escape. If you generate an interface file for a package, you can see all escaping parameters and methods annotated with ๐ŸŽ๐Ÿฅก. As an example, take a look at ๐Ÿจโ€™s ๐Ÿป:

๐ŸŒ ๐Ÿ•Š ๐Ÿจ๐ŸšElement โšช๏ธ ๐Ÿ†๐Ÿ‡
  ๐Ÿ“— Appends `item` to the end of the list in `O(1)`. ๐Ÿ“—
  ๐Ÿ– โ—๏ธ ๐Ÿป ๐ŸŽ๐Ÿฅก item Element
๐Ÿ‰

Obviously, appending a value to a list causes the value to escape, which the compiler correctly determined and annotated the parameter with ๐ŸŽ๐Ÿฅก.

In principle, you can manually annotate parameters and methods with ๐ŸŽ๐Ÿฅก, but unless you build a package with methods implemented in another language, there is no reason to do so.

Deinitializers

Emojicode allows you to define a deinitializer for your classes. A deinitializer is a function that is executed right before a class instance is destroyed. Its syntax is this:

deinitializer โŸถ โ™ป๏ธ block

We can define a deinitializer for the ๐Ÿฆ and ๐ŸŸ class, to prove the behavior we have talked about before:

๐Ÿ‡ ๐Ÿฆ ๐Ÿ‡
  ๐Ÿ†• pet ๐ŸŸ ๐Ÿ‡๐Ÿ‰

  โ™ป๏ธ ๐Ÿ‡
    ๐Ÿ˜€ ๐Ÿ”คGorilla says Bye-Bye๐Ÿ”คโ—๏ธ
  ๐Ÿ‰
๐Ÿ‰

๐Ÿ‡ ๐ŸŸ ๐Ÿ‡
  ๐Ÿ–๐Ÿ†• name ๐Ÿ”ก

  ๐Ÿ†• ๐Ÿผ name ๐Ÿ”ก ๐Ÿ‡๐Ÿ‰

  โ™ป๏ธ ๐Ÿ‡
    ๐Ÿ˜€ ๐Ÿ”คFish deinit!๐Ÿ”คโ—๏ธ
  ๐Ÿ‰
๐Ÿ‰

๐Ÿ ๐Ÿ‡
  ๐Ÿ†•๐Ÿฆ ๐Ÿ†•๐ŸŸ ๐Ÿ”คShawn๐Ÿ”คโ—๏ธโ—
๐Ÿ‰

As expected, running the above code results in:

Fish deinit!
Gorilla says Bye-Bye

You can only specify a deinitalizers for classes.

Caution

The deinitializer of a class is not called when initialization is aborted by raising an error.

Lifetime in General

We will now adjust our program and make the gorilla actually store its pet:

๐Ÿ‡ ๐Ÿฆ ๐Ÿ‡
  ๐Ÿ–๐Ÿ†• pet ๐ŸŸ

  ๐Ÿ†• ๐Ÿผ pet ๐ŸŸ ๐Ÿ‡๐Ÿ‰

  โ™ป๏ธ ๐Ÿ‡
    ๐Ÿ˜€ ๐Ÿ”คGorilla says Bye-Bye๐Ÿ”คโ—๏ธ
  ๐Ÿ‰
๐Ÿ‰

If we kept the rest of the program the same, the output will still change:

Gorilla says Bye-Bye
Fish deinit!

That is because the gorilla now maintains a reference to the fish. Thus, the compiler cannot write code to immediately destroy the fish at the end of the statement. But the gorilla can be destroyed as before. But once the gorilla is gone, also our reference to the fish is gone, so the fish is destroyed then.

The same is true when working with value types, like in the following example.

๐Ÿ•Š ๐Ÿ’ณ ๐Ÿ‡
  ๐Ÿ–๐Ÿ†• fish ๐ŸŸ

  ๐Ÿ†• ๐Ÿผ fish ๐ŸŸ ๐Ÿ‡๐Ÿ‰
๐Ÿ‰

๐Ÿ‡ ๐ŸŸ ๐Ÿ‡
  ๐Ÿ–๐Ÿ†• name ๐Ÿ”ก

  ๐Ÿ†• ๐Ÿผ name ๐Ÿ”ก ๐Ÿ‡๐Ÿ‰

  โ™ป๏ธ ๐Ÿ‡
    ๐Ÿ˜€ ๐Ÿ”คFish deinit!๐Ÿ”คโ—๏ธ
  ๐Ÿ‰
๐Ÿ‰

๐Ÿ ๐Ÿ‡
  ๐Ÿ†•๐Ÿ’ณ ๐Ÿ†•๐ŸŸ ๐Ÿ”คShawn๐Ÿ”คโ—๏ธโ—
๐Ÿ‰

We can, however, update our program slightly to see a change:

๐Ÿ ๐Ÿ‡
  ๐Ÿ†•๐Ÿ’ณ ๐Ÿ†•๐ŸŸ ๐Ÿ”คShawn๐Ÿ”คโ—๏ธโ— โžก๏ธ card
  ๐Ÿ˜€ ๐Ÿ”ค๐Ÿ’›๐Ÿ”คโ—๏ธ
๐Ÿ‰

Running this produces:

๐Ÿ’›
Fish deinit!

You can see that the ๐Ÿ’ณ is not immediately destroyed because we copied it into a variable. The variable is destroyed at the end of the scope, so is the ๐Ÿ’ณ in it. Hence we first see ๐Ÿ’› and then Fish deinit!.

Weak References

In the case of a circular reference, automatic reference counting cannot detect objects that should be deleted. A circular reference occurs if objects point at each other in a circle.

Circular references can be worked around by so called weak references. Weak references are not taken into account when counting the references left to an object and thus allow breaking up circular references.

Consider this program as an example:

๐Ÿ‡ ๐ŸŒ ๐Ÿ‡
  ๐Ÿ–๐Ÿ†• moon ๐ŸŒ•

  ๐Ÿ†• ๐Ÿผ moon ๐ŸŒ• ๐Ÿ‡
    ๐Ÿ‘‡ โžก๏ธ ๐ŸŒmoonโ—๏ธ
  ๐Ÿ‰

  โ™ป๏ธ ๐Ÿ‡
    ๐Ÿ˜€ ๐Ÿ”คEarth deinit๐Ÿ”คโ—๏ธ
  ๐Ÿ‰
๐Ÿ‰

๐Ÿ‡ ๐ŸŒ• ๐Ÿ‡
  ๐Ÿ–๐Ÿ†• earth ๐Ÿฌ๐ŸŒ

  ๐Ÿ†• ๐Ÿ‡๐Ÿ‰

  โžก๏ธ๐ŸŒ new_earth ๐ŸŒ ๐Ÿ‡
    new_earth โžก๏ธ ๐Ÿ–earth
  ๐Ÿ‰

  โ™ป๏ธ ๐Ÿ‡
    ๐Ÿ˜€ ๐Ÿ”คMoon deinit๐Ÿ”คโ—๏ธ
  ๐Ÿ‰
๐Ÿ‰

๐Ÿ๐Ÿ‡
  ๐Ÿ†•๐ŸŒ ๐Ÿ†•๐ŸŒ•โ—๏ธโ—๏ธ
๐Ÿ‰

When run, the program will exit without ever printing โ€œEarth deinitโ€ or โ€œMoon deinitโ€ as the ๐ŸŒ and ๐ŸŒ• instance a pointer at each other. Neither of them can be deleted as both have a reference count of one.

The solution is using a weak reference in one of the classes:

๐Ÿ‡ ๐ŸŒ• ๐Ÿ‡
  ๐Ÿ–๐Ÿ†• earth ๐Ÿฌ๐Ÿ“ถ๐Ÿš๐ŸŒ๐Ÿ†

  ๐Ÿ†• ๐Ÿ‡๐Ÿ‰

  โžก๏ธ๐ŸŒ new_earth ๐ŸŒ ๐Ÿ‡
    ๐Ÿ†•๐Ÿ“ถnew_earthโ—๏ธ โžก๏ธ ๐Ÿ–earth
  ๐Ÿ‰

  โ™ป๏ธ ๐Ÿ‡
    ๐Ÿ˜€ ๐Ÿ”คMoon deinit๐Ÿ”คโ—๏ธ
  ๐Ÿ‰
๐Ÿ‰

In the above program the reference from the moon back to the earth does not count when determining whether the earth instance can be deleted.

The program prints:

Earth deinit
Moon deinit

Weak references are part of the s package. See the package documentation to learn more about their usage.

Detecting Shared Values

In some cases, you might want to know whether your reference to a value is unique. This can be done by using the ๐Ÿฎ operator on the variable holding the reference.

unique โŸถ ๐Ÿฎ variable
โ† Previous Next Up: โ€œReferencesโ€ โ†’
Something not quite right? Improve this page