First of all, "no garbage collection" Technically, referencing counting is garbage collection, it's automatic memory management by definition.
Is ARC really a win over non-refcounted GC + escape analysis? Full manual control over stack vs heap allocations and lifetime, I get, especially for games. But reference counting is not deterministic, and can also generate pauses.
What LLVM static analysis can be done to figure out the lifecycle of objects also applies to other GC algorithms.
ARC may be less likely to get memory paused on release, but ARC doesn't compact free memory, and therefore fragmentation can cause object allocations (malloc) to become more expensive, possibly leading to pauses.
If you use non-copying GC that doesn't compact (ARC doesn't compact), then there are low pause GC algorithms out there that give pretty good bounds (e.g. 5ms pause).
I'd like to see actual benchmarks of both ARC and say, mark-and-sweep on a mobile device rather than speculation and opinion, and both benchmarks must get the same static compilation treatment. That is, if escape analysis tells you that the lifetime of the object is bounded by the current stack frame, then allocate it on the stack, and don't penalize the non-reference-counted GC by allocating 100% of everything on the heap.
ARC may be slower than a good GC, but what it has is deterministic behaviour. A 10ms pause could be tolerable if you know when it will happen.
A fair few years ago I worked in the murky world of J2ME games, and a common pattern was byte[] _variables = new byte[1024] so you could ensure there were no GC pauses.
> A fair few years ago I worked in the murky world of J2ME games, and a common pattern was byte[] _variables = new byte[1024] so you could ensure there were no GC pauses.
That's not the same as ARC, though. The Java equivalent to ARC would be something like having an array of volatile reference counts to go with your variables and fiddling with those at most accesses.
Not the same, but they had the same problem - GC pauses when it likes, not when you like. If you know calling move_hero() takes 23ms, 15 of which is ARC you can plan for that. You can't plan for move_hero() taking 7ms, except when GC happens.
Depending on VM, Java developers can plan where the pauses happen. One way they do that is by object pooling, another is by using DirectBuffers/off-head memory. A third way to do it is with scoped heaps.
It's not like tons of games haven't been shipped with non ref-count GC. Minecraft being the most famous, but games on Unity3D/Mono can have non-ref count GC. Lua is shipped in tons of game engines and uses classic GC.
Regardless of whether you are using automatic GC, or if you are using malloc/free, you can't write a high performance game without carefully working around memory issues. Even C/C++ games like Max Payne have shipped with frame hiccups caused by poor malloc/free behavior that had to be fixed with custom allocators.
If you're writing a game, you have to pay attention to what you're doing. But do non-framrate limited games and regular apps need ref-count GC? I suggest no, they do not.
> But do non-framrate limited games and regular apps need ref-count GC? I suggest no, they do not.
Practically the first piece of advice usually given when someone asks "how do I make my Android app non-laggy" is to avoid allocation wherever possible; even a single frame drop due to a GC pause when the user is scrolling a list, say, can be noticeable.
Azul C4 uses a custom kernel extension. It's not really a general purpose GC for user-level apps. (There are kernel-extension-free variations of it, but they suffer from reduced throughput over the HotSpot GC.)
That's true, but for a mobile device, you can control the kernel, it it may be, from a user experience point of view, that reducing pause latency is better than high throughput. I dunno, has anyone ever considered using Azul-like tricks on mobile kernels? Does ARM have the required HW to support it efficiently?
Can't cyclic reference be fixed by a garbage compiler occasionally cleaning up those things? I think that's how Python does it, not that Python is a beacon of performance, but it's possible.
@Override is optional in Java. All it does is validate that a superclass method with the same signature exists. It doesn't work in the other direction.
This doesn't compile with Swift:
class A {
func foo() {
}
}
class B : A {
func foo() {
}
}
In Java, you make the function part of an interface to achieve that:
interface Foo {
void foo();
}
class A implements Foo { ... }
class B extends A { ... }
If someone renames Foo then A and B fails to compile. If you don't want to use an interface, you do it like this:
class A {
abstract void foo();
}
class B {
void foo() { ... }
}
Yes, it's not exactly equivalent but, this is a rather contrived case that never happens in Java because
a) almost everyone uses @Override because IDEs add it automatically
b) Java programmers don't refactor by using VI and editing a base class's method names. An IDE like IntelliJ IDE does all the work automatically.
c) if you really want to ensure that a class implements a certain interface, you use a Java interface to enforce it.
This swift feature is interesting, but it's not really anything to sell the language.
Yeah it's interesting seeing the contrast in interpretations of "will change the way I program". In one world you have go which aims to truly solve one of tomorrow's biggest programming problems (concurrency), while btw never experiencing this override "bug" since it doesn't allow subclasses and circumvents entire classes of issues altogether, and on the other hand you have Swift which seems to address some rather minor qualms (IMO) with a drastic syntax change.
Swift provides high-level concurrency as well; it just does so via Grand Central Dispatch rather than any built-in language primitives. (While there are no intrinsic language features comparable to Go's channels, I was told that part of the reason for the new trailing closure syntax was so that GCD invocations would look more 'natural'.)
I don't think Swift is trying to sell itself based on the 'override' modifier, overflow-checked arithmetic, Unicode variable names, or any of the other minor features which people keep on talking about. It has a far more capable type system than Objective-C ever had. Its pattern matching is far more than "C's switch statement, but without automatic fall-through". Except for backwards-compatibility, it de-emphasizes or eschews the many at-runtime dynamic features that Objective-C had (e.g. creating and modifying classes and methods dynamically). It has tuples. It's a huge departure from Objective-C, not just incremental improvements packaged into a new language (not that it invented these new features, of course), so it's doing more than just addressing 'minor qualms'.
Okay, can someone who understands this explain it a little better. My understanding is that ARC (or automatic reference counting) wasn't garbage collection because there isn't any garbage collection. I had thought that ARC was a compiler optimization that would analyze your code and add the deconstructor code while compiling based off some possible usage graph or whatnot. ARC therefore is not garbage collection. Yes, it's memory management, but to link the two as the same is wholly inaccurate. Please chime in to let me know how wrong I am.
This is not how ARC works. ARC keeps a count (at runtime) of the number of references to dynamically allocated objects. Every time you create a new reference to an object, the count is incremented, and every time a reference goes out of scope, the count is decremented. When the count reaches zero, the memory is either freed immediately or marked as free-able for later.
Your bit about compile-time code analysis to automatically insert the cleanup code when data is no longer reachable is actually pretty similar to what Rust does today with owned pointers. The downside is that this doesn't work with "normal" code -- you need certain annotations for the compiler to be able to perform this task correctly. In Rust this is done via region pointers (lifetime parameters) and borrow checking.
The canonical text on garbage collection by probably the most respected person in the field considers reference counting (automatic or otherwise) to be GC (http://gchandbook.org/contents.html). I know that's appealing to authority, but what else can you use to prove the definition of a term? Also see [1], which argues these algorithms are all the same thing anyway.
[1] D. F. Bacon, P. Cheng, and V. T. Rajan, “A Unified Theory of Garbage Collection,” presented at the Proceedings of the 19th Conference on Object-Oriented Programming, Systems, Languages & Applications (OOPSLA), 2004.
While we can acknowledge that the more expansive definition of "garbage collection" includes ARC, we can still simultaneously hold another definition of "gc" to not include ARC when discussing Apple & Swift. That's the way Apple documentation and most others are using that term.
Apple docs:
"Garbage collection is deprecated in OS X Mountain Lion v10.8, and will be removed in a future version of OS X. Automatic Reference Counting is the recommended replacement technology."[1]
If we don't use the more common understanding of gc, Apple's documentation doesn't make sense. If we do a substitution of Apple's verbage using ARC==GC, we get nonsense such as:
"Garbage collection (which includes ARC) is deprecated in OS X Mountain Lion v10.8, and will be removed in a future version of OS X. Automatic Reference Counting (which is also part of garbage collection) is the recommended replacement technology."
With the insistence on ARC==GC, we'd have to parse that sentence as garbage collection is being removed and replaced with garbage collection.
It would be nice if we not redefine the meaning of computer terms that have be understood for decades. I know it's semantics, but I take the definitions I learned 20 years ago in my college comp-sci textbooks as the agreed upon definition.
Others would take the definitions They learned 10 or 50 years ago in their college comp-sci textbooks as the agreed upon definition, and those differ.
For example, what I learned decades ago doesn't include that 2004 paper that convincingly shows reference counting to be one end of a scale that has pure GC at the other end.
Yet, that paper made me realize that reference counting is, in some sense, garbage collection.
On the other hand, I see no big problem in having an ambiguous term. That happen all over the world, also in science. Chemists have 'alcohol' (ethanol) vs 'an alcohol' (a family of compounds that includes ethanol), mathematicians have words such as 'algebra', biologists have roses as a family of plants and as a subset thereof, etc.
Before ARC they used manual reference counting. At runtime each object has a count of how many things have declared they have a reference to it. You can increment that count by doing [obj retain] whenever you know you're keeping a reference to it that will outlive the stack frame. When you're done with the object you can do [obj release] to decrement the counter. When the counter hits zero, the memory for the object can be freed (I don't actually know if it's done immediately or added to a list of objects to free later).
What the automatic part of ARC does is, at compile time, analyse the code and automatically add the retain and release messages. If it determines the reference will outlive the stack frame, it adds a retain message. if it determines the reference can no longer be dereferenced (maybe because another object reference is assigned to that variable) it adds a release message.
The runtime characteristics are the same as ARC, it just means the programmer no longer needs to work out where to add the retain and release messages (though they still need to think about reference cycles).
It's boring to discuss if we can't make distinctions. Linear types are also reference counted; the reference count is always either 0 or 1 and it can be computed statically.
Sure, but use appropriate terminology. Automatic memory management has been synonymous with garbage collection for decades. ARC is a form of AMM/GC. So don't say Objective-C+ARC isn't garbage collection, because it is.
The proper distinction would be between local reference counting and GC at the time of release, and global heap traversal from roots.
>First of all, "no garbage collection" Technically, referencing counting is garbage collection, it's automatic memory management by definition.
Thanks for making this post, I have been meaning to say something similar for a while now. And, re: one of your other posts in this thread, the GC handbook is excellent. One of my favorite textbooks.
What most people on this site call "garbage collection" would be more accurately called "tracing garbage collection."
That's kind of irrelevant. The only thing that really matters since swift is built to natively support Cocoa, is was this possible in Objective-C (and to a lesser extent C and C++).
Is ARC really a win over non-refcounted GC + escape analysis? Full manual control over stack vs heap allocations and lifetime, I get, especially for games. But reference counting is not deterministic, and can also generate pauses.
@Override has been in Java since Java 5.