Autofree is still WIP. Until it stabilises and becomes the default, please avoid using it. Right now allocations are handled by a minimal and well performing GC until V's autofree engine is production ready.
V avoids doing unnecessary allocations in the first place by using value types, string buffers, promoting a simple abstraction-free code style.
Most objects (~90-100%) are freed by V's autofree engine: the compiler inserts necessary free calls automatically during compilation. Remaining small percentage of objects is freed via reference counting.
The developer does not need to change anything in their code. "It just works", like in Python, Go, or Java, except there's no heavy GC tracing everything or expensive RC for each object.
You can take advantage of V's autofree engine and define a
free() method on custom
Just as the compiler frees C data types with C's
free(), it will statically insert
free() calls for your data type at the end of each variable's lifetime.
Autofree can be enabled with an
For developers willing to have more low-level control, autofree can be disabled with
-manualfree, or by adding a
[manualfree] on each function that wants to manage its
See attributes for more information.
The strings do not escape
draw_text, so they are cleaned up when
the function exits.
In fact, with the
-prealloc flag, the first two calls will not result in any allocations at all.
These two strings are small, so V will use a preallocated buffer for them.
Stack and Heap
Stack and Heap Basics
Like with most other programming languages, there are two locations where data can be stored:
- The stack allows fast allocations with almost zero administrative overhead. The stack grows and shrinks with the function call depth – so every called function has its stack segment that remains valid until the function returns. No freeing is necessary; however, this also means that a reference to a stack object becomes invalid on function return. Furthermore, stack space is limited (typically to a few Megabytes per thread).
- The heap is a large memory area (typically some Gigabytes) that is administrated by the operating system. Heap objects are allocated and freed by special function calls that delegate the administrative tasks to the OS. This means that they can remain valid across several function calls; however, the administration is expensive.
V's default approach
Due to performance considerations, V tries to put objects on the stack if possible but allocates them on the heap when obviously necessary. Example:
a is stored on the stack since it's address never leaves the function
However, a reference to
b is part of
e which is returned. Also, a reference to
c is returned. For this reason
c will be heaped allocated.
Things become less obvious when a reference to an object is passed as function argument:
Here the call
q.f(&w) passes references to
b is of type
f()'s declaration, so technically
these references are leaving
main(). However, the lifetime of these
references lies inside the scope of
w are allocated
on the stack.
Manual Control for Stack and Heap
In the last example the V compiler could put
w on the stack
because it assumed that in the call
q.f(&w) these references were only
used for reading and modifying the referred values – and not to pass the
references themselves somewhere else. This can be seen in a way that the
w are only borrowed to
Things become different if
f() is doing something with a reference itself:
f() looks quite innocent but is doing nasty things – it inserts a
r. The problem with this is that
s lives only as long
g() is running but
r is used in
main() after that. For this reason
the compiler would complain about the assignment in
refer to an object stored on stack". The assumption made in
g() that the call
r.f(&s) would only borrow the reference to
s is wrong.
A solution to this dilemma is the
[heap] attribute at the declaration
struct MyStruct. It instructs the compiler to always allocate
on the heap. This way the reference to
s remains valid even after
The compiler takes into consideration that
MyStruct objects are always heap
allocated when checking
f() and allows assigning the reference to
s to the
There is a pattern often seen in other programming languages:
f() is passed a reference
a as receiver that is passed back to the caller and returned
as a result at the same time.
The intention behind such a declaration is method chaining like
y = x.f().g().
However, the problem with this approach is that a second reference
a is created – so it is not only borrowed and
MyStruct has to be
In V, the better approach is:
This way the
[heap] attribute can be avoided – resulting in better performance.
However, stack space is very limited as mentioned above. For this reason the
attribute might be suitable for very large structures even if not required by use cases
like those mentioned above.
There is an alternative way to manually control allocation on a case-to-case basis. This approach is not recommended but shown here for the sake of completeness:
Here the compiler check is suppressed by the
unsafe block. To make
s be heap
allocated even without
[heap] attribute the
struct literal is prefixed with
This last step would not be required by the compiler but without it the reference
r becomes invalid (the memory area pointed to will be overwritten by
use_stack()) and the program might crash (or at least produce an unpredictable
final output). That's why this approach is unsafe and should be avoided!