Ownership You Can Count On: A Hybrid Approach To Safe Explicit Memory Management
Ownership You Can Count On: A Hybrid Approach To Safe Explicit Memory Management
You Can Count On:
A Hybrid Approach to Safe Explicit Memory Management
Adam Dingle David F. Bacon
Google IBM T.J. Watson Research Center
1600 Amphitheatre Parkway 19 Skyline Drive
Mountain View, CA 94043 Hawthorne, NY 10532
+1 (415) 4256891 +1 (914) 7847811
adamdingle@gmail.com dfb@watson.ibm.com
void Fun() { When the method exits, the variable n goes out of scope and
Foo [] ^a = new Foo[5]; // an owned array the list is destroyed automatically. Initially, the first list node has a
of non-owning pointers to Foo objects non-owning reference count of 1 since the second list node points
Foo ^[] ^b = new Foo^[5]; // an owned array to it. Before checking the first node's reference count, Gel
of owning pointers to Foo objects destroys the second list node, which destroys the non-owning
Foo ^[] c = new Foo^[5]; // a non-owned pointer to the first node and hence decrements the first node's
array of owning pointers to Foo objects
b[0] = new Foo(); reference count to 0.
a[0] = b[0]; In general, then, Gel can automatically destroy any data
} structure in which every non-owning pointer from an object o
points to an object which is an ancestor of o in the object
The take operator may operate on array elements: ownership tree.
Gel's object destruction algorithm has some significant
void Fun() { limitations. Most fundamentally, it cannot destroy arbitrary graphs
Foo ^[] ^a = new Foo^[5]; of non-owning pointers; if a data structure contains non-owning
a[0] = new Foo(); pointers to non-ancestor nodes, then the programmer must write
Foo ^f = take a[0]; // a[0] is null after code to null out those pointers before the structure is destroyed.
the take Another problem is that the algorithm may use a large amount of
} stack space when destroying objects due to its recursive nature. In
2.7 Autoboxing the Extensions section, below, we propose a better algorithm for
Gel (like Java 5 and C#) provides autoboxing: a simple value destroying objects in Gel.
such an int or bool may be stored in a variable holding an 2.9 Type Conversions
object. In Gel, a boxed value always has the owning type object In the sections above we've described how ownership interacts
^. For example: with various Gel language features, and presented various
examples where Gel allows or disallows conversions between
void Fun() {
object ^o = 7; // ok owning and non-owning types. In this section we give a more
object p = false; // compile-time error: complete description of Gel's rules for type conversions.
can't convert from bool to object Gel classifies each expression in a program as one of the
} following kinds:
In an owning type T ^, T must not be a value type; the types • A variable: either
int ^ and bool ^ do not exist in Gel, for example. An • a local variable or method parameter variable
unboxing conversion must be explicit, and yields a value type: • (type) E, where E is classified as a variable
• E as type, where E is classified as a variable
void Fun() { • E ? E1 : E2, where E1 and E2 are classified as
object ^o = 7; variables
int i = (int) o; • A field: either
} • an access to a instance field or static field
2.8 Object Destruction • an access to an array element
Whenever an owning pointer to an object o is destroyed • A value: any other expression
without transferring ownership elsewhere, then Gel destroys o as
follows. First Gel destroys all fields in o (in reverse lexical order). Gel, like C# and Java, includes both implicit and explicit type
If any of these fields are owning pointers to other objects then conversions. A number of language constructs implicitly convert a
value to a destination type. Explicit type conversions are pointer retains ownership and a new non-owning pointer is
performed by the type cast operator and by a small number of created. The conversion rules disallow conversions which would
other language constructs. inevitably lead to object destruction errors at run time. As an
Every implicit conversion in Gel takes place in one of the example, consider the following method:
following conversion contexts:
Foo Fun() {
• Assignment context: in an assignment V = E, when return new Foo();
implicitly converting from the type of E to the type of V }
• Argument conversion context: in a method call, when The type conversion from Foo ^ to Foo occurs in a return
implicitly converting a method argument to its context, and is hence disallowed. Gel could theoretically allow
corresponding parameter type this type conversion and generate code for the method. Then at
• Return context: when converting the expression in a return method exit the newly allocated Foo object would be destroyed
statement to the method's return type (since it is owned by a temporary owning pointer, and ownership
• Other context: used for a small number of additional hasn't transferred elsewhere). But the destroyed object's non-
conversions owning pointer count would be 1, representing the non-owning
pointer returned from the method, and so the object destruction
We now present Gel's implicit and explicit conversion rules. would yield a run-time error.
(We omit rules relating to conversions between value types, such
as between int and char; these rules are detailed in the Gel
2.10 Erasure
reference manual and are similar to value conversion rules in C#.) Note that Gel adds only two syntactic elements to C#: the type
constructor ^ and the operator take. Suppose that we apply the
2.9.1 Implicit conversions following erasure transformation to a Gel program P:
Every type T is implicitly convertible to itself.
If a type T is derived from a type U, then T is implicitly • We remove every instance of the ^ type constructor: we
convertible to U. replace T ^ with T everywhere.
The null type is implicitly convertible to any reference type or • For each instance take E of the take operator, let T ^ be
owning type. the compile-time type of E. Then we replace take E with
T ^ is implicitly convertible to U ^ if T is implicitly the function call Take(ref E), where Take is defined
convertible to U. as follows:
In an argument conversion or local assignment context, T ^ is
implicitly convertible to U if T is implicitly convertible to U, and
T Take(ref T p) {
either T v = p;
p = null;
• the conversion is in an argument conversion context, or return v;
• the conversion is in an assignment context, and the source }
expression is classified as a variable.
(Informally, this is a function which works like the take
operator but acts on a non-owning type).
Any value type is implicitly convertible to object ^. In an Then the resulting program P' is a valid C# program. If P
argument conversion context, any value type is also implicitly terminates without error, then P' will also terminate without error
convertible to object. These conversions are boxing conversions. and will yield the same value as P.
In a compiled Gel program, a boxing conversion allocates a boxed Note that the converse is not true: it is possible that P' may
object instance. terminate without error, but that P will yield a run-time error. To
string is implicitly convertible to object ^ and to see this, consider the following Gel function:
object .
2.9.2 Explicit conversions int Fun() {
If T is implicitly convertible to U, then T is explicitly Foo ^f = new Foo();
convertible to U. Foo g = f;
f = null;
If type T is derived from a type U, then U is explicitly return 4;
convertible to T. This conversion will fail if the source value is not }
an instance of T.
T ^ is explicitly convertible to U ^ if T is explicitly convertible This function will yield a run-time error in Gel, since at the
to U. moment f is destroyed there is still a non-owning pointer g to the
object and object ^ are explicitly convertible to any same object. But if we apply the erasure transformation to this
value type T; this is an unboxing conversion. This conversion will function, then the resulting C# function will execute without error.
fail if the source value is not actually an instance of T.
object and object ^ are explicitly convertible to
3. PROGRAMMING IN GEL
string. This conversion will fail if the source value is not We have described the Gel language. We must now address
actually a string. two questions: how easy is it to use Gel's type system for writing
real programs, and how well will Gel programs perform? We
2.9.3 Discussion discuss usability in this section and performance in following
Gel never allows a type conversion to a non-owning to an sections.
owning pointer type. A conversion from an owning to a non-
owning pointer is allowed only in certain contexts as per the rules
above; when such a conversion occurs, at run time the source
3.1 Data structures intentionally create a cycle of owning pointers as in the
We believe that many programmers writing in languages such MakeACycle method above, then break the the cycle manually
as C++ write code which frees objects based on ownership when deciding to destroy the list.
relationships between objects. For example, most C++ 3.3 Experience writing the compiler in Gel
programmers have written destructors which free objects pointed We've only written one large program in Gel, namely the Gel
to by the destroyed object. The popular scoped_ptr template class compiler/interpreter itself. In writing the Gel compiler we found
from the Boost ++ library implements the ownership relationship; that most objects had natural owners; ownership works
when a scoped_ptr is destroyed the object it points to is freed particularly well for trees of objects such as the abstract syntax
automatically. In some sense, then, Gel takes the existing informal tree generated by parsing. Certain objects' lifetimes were
notion of object ownership and encodes it into the type system. indefinite, and we generally chose to allocate such objects from
But how easy is it to find a single owner for every object in a pools as described above; we used non-owning pointers to such
program as Gel's type system requires? Fortunately many objects throughout the program.
common data structures such as lists and trees have a natural The Gel implementation includes both a compiler and an
hierarchical structure. Many programs contain hierarchies of interpreter. In implementing the interpreter, we chose to represent
objects linked in parent/child relationships; in these programs, owning pointers in the interpreted language using owning pointers
pointers from parents to children can be owning, and backpointers in the interpreter itself. This means that the interpreter has no code
from children to parents can be non-owning. to free objects in the interpreted program explicitly; instead,
Some data structures are not naturally hierarchical and may in objects in the interpreted program are freed automatically when
fact involve arbitrary pointer graphs. To represent an arbitrary no longer needed. (This is similar to implementing an interpreter
graph, a Gel program can allocate all graph nodes from an array for a garbage-collected language in a garbage-collected language,
whose only purpose is to hold an owning pointer to each node; we in which case the interpreter itself need not concern itself with
call such an array a pool. The program can then use non-owning garbage collection, which "flows through" the interpreter to the
pointers between graph nodes arbitrarily. When the owning pool interpreted program.)
is destroyed, all graph nodes will be destroyed as well. (The Gel The interpreter contains numerous code paths which must
implementation, in fact, includes a built-in type pool which can manipulate values in the interpreted language which might either
be used for allocations of this sort; for details, see the Gel be owned or unowned pointers. To handle this situation, the
language reference.) Note that a C++ program implementing an interpreter code has a class RValue with two subclasses:
arbitrary graph must also generally keep all graph nodes in a list GValue, which holds a value directly, and Reference, which
in order to be able to free each node exactly once. holds a non-owning pointer to a GValue. Code which
manipulates values in the interpreted language generally
3.2 Circular data structures represents them using a variable of type RValue ^. If this
It's possible for a Gel program to create cycles of owning underlying value is owning, the variable holds the underlying
pointers. The following program creates two Bar objects which value directly; if it is not, the variable holds a Reference
point to each other: pointing to the value. This was slightly awkward. We hope that
this particular coding difficulty was an artifact of implementing a
class Bar { Gel interpreter in Gel itself, and that most programs in Gel will
Bar ^next; not need to manipulate values whose ownership status is unknown
static void MakeACycle() { at compile time.
Bar ^n = new Bar();
Bar ^o = new Bar(); 4. REFERENCE COUNT OPTIMIZATION
Bar p = o; As described above, Gel keeps a count of non-owning pointers
n.next = o; to every object. Gel's reference counts are significantly cheaper
p.next = n; than those in a traditional reference-counted system, for three
} reasons:
}
1. Gel doesn't need to keep reference counts for owning pointers;
These objects will never be destroyed. Gel only destroys an object's reference count doesn't change as it is passed from
structures containing no owning pointer cycles; this is a weaker owner to owner. In a traditional reference-counting system, a
guarantee than that provided by a garbage collector, which can reference count is kept for every pointer to an object.
destroy structures containing arbitrary pointer cycles. 2. In Gel, a reference count decrement is simply an integer
But this hardly matters in practical programming. It's hard to decrement, which may well be a single instruction. A
create an owning pointer cycle by mistake in Gel, because the type traditional system must check whether a reference count is
system guarantees that a programmer can't create an owning zero after each decrement; in Gel this is unnecessary because
pointer cycle and return an owning pointer to it. For example, if ownership, not the reference count, determines when it's time
we modify the MakeACycle method above to return a Bar^ and to free an object.
add the line return n; at the end of the method, we receive a 3. The Gel compiler can use ownership information to optimize
compile-time error: we can't return n because ownership has away many reference counts at compile time in a single-
already transferred away from it (in the assignment to p.next). threaded Gel program.
All common data structures can be coded without using
owning pointer cycles. An inherently circular structure such as a
circular linked list is a special case of an arbitrary graph; as This last point deserves more explanation. The Gel compiler
described above, the Gel programmer can allocate all list nodes implements a reference count elimination optimization based on
using a separate owner object and then create the circular links the following idea. Suppose that, for a type T, a given block of
using non-owning pointers. Alternatively, a programmer can code never mutates any pointer of type U ^, where U is T, any
superclass of T, or any subclass of T. Then the code cannot • self-compile: compiles a 73,000-line Gel program
possibly destroy any object which can be held in a variable of type (consisting of 10 copies of the Gel compiler concatenated
T, and therefore any non-owning variable or temporary of type T together, with classes renamed in each copy to avoid
whose lifetime is contained in the block of code need not hold a duplicate class definitions)
reference count.
As an example, consider the following loop, which iterates We've measured the performance of these benchmarks on a
down a linked list, adding the integer values which are found in computer with a 1.83 Ghz Intel Core 2 Duo processor and 2 Gb of
each node of the list: RAM, running Ubuntu Linux 6.10. The C++ benchmarks (and the
C++ code output by the Gel compiler) were compiled using gcc
Node ^first = construct();
int sum = 0; 4.1.2 using the -O2 optimization level. We used the Sun JDK
for (Node n = first ; n != null ; n = 1.5.0_08 to compile and run the Java benchmarks; we used the
n.next) "server" (not "client") VM from the JDK. We compiled and ran
sum += n.i; the C# benchmarks using version 1.1.17.1 of Mono [22], an open-
source implementation of C#. Note that Mono uses the Boehm
A traditional reference-counted implementation would garbage collector [21] .
increment and then decrement the reference count of each node in For each benchmark run, we collected the following statistics:
the list as it is pointed to by the loop variable n. The Gel compiler
can optimize these reference counts away: the variable n is live • CPU time (including both user and system time) in seconds.
only during the loop execution, and no code in the loop mutates a • Maximum virtual memory. This includes all pages mapped
owning pointer of type T ^, where T is Node or any superclass or into the process's address space, including those not
subclass of it (indeed, the code mutates no owning pointers at all.) resident in physical memory, and including both private and
And so no Node object can possibly be destroyed during the loop. shared pages.
The compiler implements this optimization interprocedurally. • Maximum resident memory. This includes only pages
Suppose that we modify the loop body above to call n.Get(), resident in physical memory. (Both private and shared pages
where the Get() method merely returns the instance variable i. are included.)
Then the optimization still holds, since the compiler recognizes • Maximum private writeable virtual memory. This includes
that the Get() method also mutates no owning pointers of type only pages which are private to the process and are
Node. writeable. (Both resident and non-resident pages are
To implement this optimization, the compiler generates a call included.)
graph of all methods in the program being compiled. Using this
graph, the compiler computes, for each method, the set of owning We collected the virtual memory statistics using ptime, a
types which may be mutated during a call to M. When compiling program we wrote specifically for Linux; its source code is
each method, the compiler generates a graph of control flow in the available on the project site.
method. For each non-owning local variable (or temporary value Here are the benchmark results; a discussion follows below.
in a expression), the compiler examines the control flow nodes
where the variable is live and generates the set of owning types Benchmark: sort 5
which may be mutated during the variable's lifetime, including
types which may be mutated during method calls invoked from CPU (sec) virtual (Mb) resident (Mb) private (Mb)
those nodes. If none of these types are a supertype or subtype of C++ 7.1 18.3 16.5 15.9
the variable's own type, the compiler does not bother to count
references from the variable to any object. Java 5.2 709.5 74.7 651.4
5. BENCHMARK PERFORMANCE C# 3.3 42.3 27.4 30.4
To compare Gel's performance with that of languages using
manual memory management and languages using garbage Gel 4.8 18.3 16.6 15.9
collection, we've implemented several small benchmark programs
in C++, Java, C# and Gel. The source code to these benchmarks is
available at the Gel project site. The benchmarks include the Benchmark: sortstring 5
following:
CPU (sec) virtual (Mb) resident (Mb) private (Mb)
• sort: on each of a series of iterations, generates a list of C++ 4.4 25.4 23.6 23.0
1,000,000 randomly generated integers and sorts them using
a recursive mergesort algorithm. Java 6.5 708.0 127.8 650.8
• sortstring: like the sort benchmark, but uses a list of
C# 6.8 50.8 33.5 38.9
400,000 randomly generated strings.
• binarytrees: allocates and frees a large number of Gel 5.1 34.8 33.0 32.4
binary trees of various sizes. This program is from the
Computer Language Shootout Benchmarks at
http://shootout.alioth.debian.org/.
Benchmark: self-compile • Gel does not let the programmer define a destructor for a
CPU (sec) virtual (Mb) resident (Mb) private (Mb) class, i.e. a method which will run when an instance of the
class is destroyed.
C# 5.3 80.2 67.4 66.5 • Gel can automatically destroy structures in which each
internal non-owning pointer points to an ancestor in the
Gel 5.6 68.7 66.8 65.6 ownership tree, but cannot automatically destroy structures
containing arbitrary non-owning pointers to internal nodes.
Memory usage was remarkably consistent across the • Object destruction in Gel may consume a large amount of
benchmarks. For every memory statistic in every benchmark, Java stack space since it is inherently recursive.
consumed the most memory, followed by C#, followed by Gel,
followed by C++. In every benchmark except for sortstring, To overcome these limitations, we propose adding destructors
Gel's memory consumption was only slightly greater than C++'s. to Gel, and propose a multi-phase object destruction mechanism.
In the sortstring benchmark Gel used about 40% memory Gel could destroy an object graph rooted at a pointer P in the
than C++; this is presumably because Gel represents a string using following phases.
a structure containing a reference count and a character pointer,
whereas the C++ version of the benchmark represents a strings 1. Recursively descend all objects in the ownership tree
using a character pointer alone. In this benchmark Gel below P, running each object's destructor.
nevertheless used significantly less virtual memory than C# and 2. Recursively descend again; for each non-owning pointer
significantly less physical memory than Java. in every object, decrement the reference count of the
CPU usage was not nearly so consistent across benchmarks. In object pointed to.
two benchmarks C++ was faster than both Java and C#; in one 3. Recursively descend again; for each object, check that
benchmark both Java and C# were faster; and in one benchmark its reference count is zero and free the object.
(binary-trees) Java was, surprisingly, over twice as fast as
C++, but C# was much slower. Gel's performance relative to C++
In many cases the compiler should be able to optimize away
also varied from benchmark to benchmark. In sortstring Gel was
some or all of the work to be performed in the first and/or second
about 15% slower; in binary-trees Gel was about 70%
phases. In particular, using static type analysis it will often be
slower. In the sort benchmark Gel was, oddly, faster than C++;
possible to determine that all objects in the graph below an object
this is surprising because the Gel compiler generates C++ code.
of type T will never have any destructors or non-owning pointers;
Perhaps the code generated by the Gel compiler triggers some
in that case the first or second phases need not run at all below
optimization which doesn't occur when the C++ version is
such objects. As a simple example, consider a singly linked list in
compiled; we hope to have an explanation for this by the time the
which each node contains only an integer and an owning pointer
final version of this paper is published.
to the next node; then the first and second phases can be optimized
5.1.1 Effectiveness of reference count elimination away entirely in destroying a list of this type.
To measure the effect of Gel's reference count optimizer, we Note also that in many structures each node contains only a
instrumented our benchmark programs to report the number of single owning pointer; in such structures, the compiler can save
reference count increments which occur in each benchmark run. stack space by eliminating tail recursion in generating the code to
The benchmark results follow: perform the recursive descent in each phase. With these
Effect of reference count optimization optimizations in place, we believe that this generalized destruction
algorithm might outperform the algorithm in the existing Gel
with optimization without optimization
benchmark implementation in many cases.
cpu (sec) incr ops cpu (sec) incr ops User-written destructors might potentially mutate the graph of
objects being destroyed. If a destructor inserts objects into the
sort 4.8 93.4M 5.9 868.2M graph, then these objects might be destroyed in phases 2 and 3
even though their destructors have never run. Similarly, if a
sortstring 5.1 34.7M 5.7 326.6M destructor removes objects whose destructors have already run,
binarytrees 17.8 0 20.2 471.1M then those objects' destructors might run again when they are
destroyed at a later time. This possibility does not compromise the
self-compile 5.6 13.0M 6.9 225.7M safety of the language, but does mean that each object's destructor
is not guaranteed to run exactly once. It might be possible to
detect destructor-induced mutations in some way and report an
error if they occur; we will not discuss this possibility further 7. RELATED WORK
here. Our work represents one of many attempts to find a "sweet
6.2 Polymorphism spot" in the trade-off space between fully manual and fully
The Gel type system does not support any sort of automatic memory management (that is, garbage collection).
polymorphism between owning and non-owning types. This There are many inter-related issues, chief among them being
means that it's not possible to write a method which can receive memory footprint, run-time cost, determinism, and ease of
either an owning or non-owning pointer as an input parameter. It's programming. We compare our work to that of others across these
also not possible to implement a single data structure (such as a various axes.
tree or hash table) which can hold either owning or non-owning Region-based memory management [15] provides bulk-
pointer. This limitation is reflected in the Gel class library, which freeing of objects, often based on a stack discipline. Regions
contains separate ArrayList and NonOwningArrayList generally provide low cost and good determinism, but if provided
classes. in a safe manner either require either burdensome annotation [19]
It would be useful to extend Gel with generic types as found or an analysis which is frequently over-conservative [2]. Region
in Java and C#, allowing a generic type to represent either an systems that follow a stack discipline often suffer from large
owning or non-owning type. As a simple example, suppose that numbers of object being assigned (by analysis or by the
we'd like to implement a class Pair<T> which holds two objects programmer) to the global region which is never collected (or
of type T. We'd like to be able to instantiate the class using either must be collected by a supplementary garbage collector).
an owning or non-owning pointer type. Our generic type system The Real-time Specification for Java [3] provides a stack-
might allow this as follows: oriented region-based memory management based on scopes.
Scopes have neither annotations nor automatic inference; instead
class Pair<T> { it is the responsibility of the programmer to allocate objects in the
T first, second; correct scope. Storing a pointer that violates the stack discipline of
Pair(T first, T second) { scopes causes a run-time exception. Scopes are thus type-safe but
this.first = first; subject to run-time exceptions that are difficult to prevent and are
this.second = second;
} highly dependent on interactions between different components.
T% First() { return first; } In addition, it is often either convenient or necessary to place
T% Second() { return second; } objects in the outermost scope (called Immortal memory), in
} particular when it is necessary to share objects between scope-
managed and garbage-collected memory.
The system includes a type operator % which can be applied Gay and Aiken [12] suggest the use of reference counted
only to a generic type, and which removes ownership from a type. regions that need not follow a stack discipline. Safety is ensured
In particular, if T is Foo ^, then T% is Foo; if T is Foo, then T% is because explicit freeing is not allowed to succeed unless the
also Foo. region's reference count has dropped to zero. They include
A generic type system for Gel should also support covariance: annotations for specifying that pointers are in the same region or
it should be possible to write a single function which can take are pointers to parent regions; updates of such pointers do not
either a Pair<Foo> or a Pair<Foo ^> as a parameter, and change reference counts. Although the granularity and abstraction
similarly it should be possible to write a single function which can level are different from ours, these annotations also allow them to
take either a Foo[] or a Foo^[] as a parameter. optimize away a large fraction of reference counting operations.
We believe that it should not be difficult to design a complete The goals of the Cyclone language [17] are probably most
generics system for Gel including covariance, but we will not similar to those of Gel; its goal is to provide a safe alternative to C
explore the idea further in this work. for systems programming. Cyclone provides several forms of safe
6.3 Multithreading memory management: regions, unique pointers, and reference
As described above, the Gel compiler's reference count counting. Cyclone's regions require explicit annotations in the
elimination optimization will work only in single-threaded source code, and can be both lexically scoped (in the style of
programs; this is a significant limitation. [Tofte 1]) or dynamically scoped. In the latter case, if a region is
It would be useful to extend Gel to be able to execute freed dynamically subsequent attempts to access objects in that
multithreaded programs safely and efficiently. We can imagine region will generate a run-time exception.
two different possible approaches here. First,it might be possible Cyclone's unique pointers are based on linear types [13] and
to extend Gel's optimizer to be able to infer that many data are essentially like Gel's owning pointers, but without the
structures are accessed only from a single thread; then reference capability to reference count non-owning references. Some
counts in such structures could be optimized away using the polymorphism across memory management styles is provided by
existing algorithm. Such inference seems difficult and we are allowing sets of unique pointers to be temporarily treated as
relatively pessimistic about such an approach. though they belong to a region. Cyclone also provides explicit (but
As another approach, we could possibly extend Gel so that safe) reference counting; reference counted objects are treated as
objects are grouped into apartments which may be accessed from belonging to a special region called RC.
only a single thread at once. With this approach, the existing The experience of the language designers in writing the
optimization algorithm could be used to eliminate reference count Cyclone compiler is instructive: first coarse-grained regions were
accesses in code which executes inside a single apartment. It used, which reduced the annotation burden. However, coarse
might be possible to design system which guarantees that only regions did not free memory soon enough and led to excessive
pointers of a certain kind may cross apartment boundaries and space consumption. The compiler was then rewritten using fine-
which allows groups of objects to move efficiently between grained regions, at which point the annotations became overly
apartments; we leave this to further work. cumbersome. Eventually the compiler was rewritten to use
garbage collection [Greg Morrisett, personal communication].
Ownership types have received considerable attention in [5] Boyapati, C., Liskov, B., and Shrira, L. Ownership types for
recent years. Unlike Gel's concept of ownership, these approaches object encapsulation. In Proceedings of the 30th ACM
rely entirely on the type system [5] [7] [9]. Ownership is used for SIGPLANSIGACT Symposium on Principles of
a variety of purposes: modularity [8], avoidance of run-time Programming Languages (New Orleans, Louisiana, USA,
errors, improved alias analysis and optimization [9], prevention of January 15 17, 2003). ACM Press, 213223.
data races and deadlocks [4], and real-time memory management
[6] [20]. Generally speaking, these approaches have a high [6] Boyapati, C., Salcianu, A., Beebee, W., and Rinard, M.
annotation overhead and/or require significant redundant code due Ownership types for safe regionbased memory management
to the coupling of types to ownership contexts. Clarke and in realtime Java. In Proceedings of the ACM SIGPLAN 2003
Wrigstad [10] also point out that ownership types often require Conference on Programming Language Design and
external interface changes in response to purely internal changes Implementation (San Diego, California, USA, June 09 11,
in data structures; they attempted to alleviate this problem using a 2003). ACM Press, 324337.
combination of unique pointers and ownership types, but the
resulting language still suffers a high annotation burden. [7] Boyland, J. Alias burying: unique variables without
Fahndrich and DeLine [11] suggest adoption and focus as destructive reads. Softw. Pract. Exper. 31, 6 (May. 2001),
constructs for incorporating linear types with an abiity to 533553.
temporarily create and relinquish aliases, and successfully applied [8] Clarke, D. G., Noble, J., and Potter, J. Simple Ownership
it to the writing of low-level systems code. This provides very Types for Object Containment. In Proceedings of the 15th
strong static information, but requires significant and not always European Conference on ObjectOriented Programming
intuitive annotation.
(June 18 22, 2001). SpringerVerlag, 5376.
Real-time garbage collection [1] [16] provides overall timing
determinism and ease of programming, but suffers from the [9] Clarke, D. G., Potter, J. M., and Noble, J. Ownership types
memory overhead issues associated with garbage collection in for flexible alias protection. In Proceedings of the 13th ACM
general, and does not provide determinism in terms of the times at SIGPLAN Conference on ObjectOriented Programming,
which individual objects are freed. Also, during garbage collection Systems, Languages, and Applications (Vancouver, British
some fraction of the CPU time (on the order of 30%) is Columbia, Canada, October 18 22, 1998). ACM Press, 48
unavailable to real-time tasks. 64.
[10] Clarke, D., and Wrigstad, T. External uniqueness is unique
8. ACKNOWLEDGEMENTS enough. In European Conference on ObjectOriented
We'd like to thank Linus Upson for his invaluable feedback and Programming (ECOOP). , 176–200.
encouragement. Discussions with Paul Haahr were also useful.
Feng Qian ported the Gel compiler from Windows to Linux. Many [11] Fahndrich, M. and DeLine, R. Adoption and focus: practical
thanks are due to Google for allowing us to release the Gel linear types for imperative programming. In Proceedings of
compiler as open source. the ACM SIGPLAN 2002 Conference on Programming
Language Design and Implementation (Berlin, Germany,
9. REFERENCES June 17 19, 2002). ACM Press, 1324.
[12] Gay, D. and Aiken, A. Language support for regions. In
[1] Bacon, D. F., Cheng, P., and Rajan, V. T. A realtime garbage Proceedings of the ACM SIGPLAN 2001 Conference on
collector with low overhead and consistent utilization. In Programming Language Design and Implementation
Proceedings of the 30th ACM SIGPLANSIGACT Symposium (Snowbird, Utah, United States). ACM Press, 7080.
on Principles of Programming Languages (New Orleans,
Louisiana, USA, January 15 17, 2003). ACM Press, 285 [13] Girard, J. Linear logic. Theoretical Computer Science 50:1
298. (1987), 1102.