class as an interface. This would, however, make it impossible to inherit
from the bridged classes without special compiler support to understand
that some interfaces came along with
Although complex, this is a simpler system than interfacing between
languages that differ on what method
lookup means. For example, Java and
Smalltalk have almost identical object
and memory models, but Java ties the
notion of method dispatch to the class
hierarchy, whereas in Smalltalk two
objects can be used interchangeably
if they implement methods with the
This is a problem encountered by
RedLine Smalltalk, 1 which compiles
Smalltalk to run on JVM. Its mechanism for implementing Smalltalk
method dispatch involves generating
a Java interface for each method and
then performing a cast of the receiver
to the relevant interface type before dispatch. Sending messages to Java classes requires extra information, because
existing Java classes do not implement
this; thus, RedLine Smalltalk must fall
back to using Java’s Reflection APIs.
The method lookup for Smalltalk
(and Objective-C) is more complex,
because there are a number of second-chance dispatch mechanisms that are
either missing or limited in other languages. When compiling Objective-C
Java Script method invocation, you must
wrap each Objective-C message send in
a small function that first checks if the
method actually exists and, if it does
not, calls some lookup code.
because it handles variadic functions
in a convenient way: if a function or
method is called with more arguments
than it expects, then it receives the remainder as an array that it can expect.
Go does something similar. C-like languages just put them on the stack and
expect the programmer to do the right
thing with no error checking.
The obvious dichotomy in memory
models is between automatic and
manual deallocation. A slightly more
important concern is the difference between deterministic and nondeterministic destruction.
It is possible to run C with the
Boehm-Demers-Weiser garbage collector3 without problems in many
cases (unless you run out of memory
and have a lot of integers that look like
pointers). It is much harder to do the
same for C++, because object deallocation is an observable event. Consider
the code shown in Figure 1.
The LockHolder class defines a
very simple object; a mutex passes
into the object, which then locks the
mutex in its constructor and unlocks
it in the destructor. Now, imagine
running this same code in a fully
time at which the destructor runs is
This example is relatively simple
to get right. A garbage-collected C++
implementation is required to run
the destructor at this point but not to
deallocate the object. This idiom is
not available in languages designed to
support garbage collection from the
start. The fundamental problem with
mixing them is not determining who
is responsible for releasing memory;
rather, it is that code written for one
model expects deterministic operation, whereas code written for the other does not.
There are two trivial approaches to
implementing garbage collection for
C++: the first is to make the delete
operator invoke destructors but not
reclaim the underlying storage; the
other is to make delete a no-op and
call destructors when the object is detected as unreachable.
Destructors that call only delete
are the same in both cases: they are
effectively no-ops. Destructors that release other resources are different. In
the first case, they run deterministically but will fail to run if the programmer
does not explicitly delete the relevant
object. In the second case, they are
guaranteed to run eventually but not
necessarily by the time the underlying
resource is exhausted.
Additionally, a fairly common idiom
in many languages is a self-owned object that waits for some event or performs a long-running task and then
fires a callback. The receiver of the
callback is then responsible for clean-ing up the notifier. While it is live, it
is disconnected from the rest of the
object graph and so appears to be garbage. The collector must be explicitly
told that it is not. This is the opposite
of the pattern in languages without automatic garbage collection, where objects are assumed to be live unless the
system is told otherwise. (Hans Boehm
discussed some of these issues in more
detail in a 1996 paper. 2)
All of these problems were present
with Apple’s ill-fated (and, thankfully,
no longer supported) attempt to add
garbage collection to Objective-C. A
lot of Objective-C code relies on running code in the -dealloc method.
Another issue was closely related to the
problem of interoperability. The implementation supported both traced and
untraced memory but did not expose
this information in the type system.
Consider the snipped noted in Figure 2.
figure 1. c++ code using deterministic automatic deallocation to relinguish a lock.
LockHolder l( mutex );
/* Do stuff that requires mutex to be locked */
figure 2. objective-c code demonstrating the problems with retrofitting garbage
collection to an existing language.
void allocateSomeObjects (id buffer , int count )
for (int i=0 ; i< count ; i++)
buffer [i] = [ SomeClass new ];