1,513 karma · joined October 13, 2010
If fork() performance is important to you, you probably don't want compaction for the tenured generation at all. That's totally fine; while fragmentation will accumulate over time, it's no worse than C++. You'll still have copying for the nursery, but the nursery fills up and gets recycled so often that the COW optimization isn't really helping you there anyhow.
[1] Language developers sometimes claim they don't need it. I believe they're on the wrong side of history.
[2] Forks are common for this, and for good reason; often times languages that start with reference counting end up taking large dependencies on external libraries that manipulate reference counts (like Python with its Py_INCREF and Py_DECREF), at which point the language can't fully move to tracing GC without breaking its ecosystem.
The alternatives are either (a) avoiding shared state; (b) strong lifetime management such that you make sure one parent thread owns all of the data that its child threads are going to use; (c) concurrent GC; (d) pretending it's not a problem like Go does and doing stop-the-world GC.
The exception is the autorelease pool, which gets dumped at the end of every turn of the event loop. You can promote or demote objects from the autorelease pool at will.
Anyway, the biggest problems with Obj-C memory management are (a) you can't allocate objects on the stack, so you're forced into using the autorelease pool for stuff, and (b) you can promote or demote objects from the autorelease pool at will, which means that even the autorelease pool has to malloc, thus you don't get the nice benefits of super-cheap bump allocation that C++ arenas (or a generational GC's nursery) buy you.
Also, to answer your query, Rust uses unique pointers (the equivalent of std::unique_ptr in C++0x) for inter-thread communication. When the unique pointer goes out of scope, its memory is immediately freed (since it was the only owner of the data). And since the data has only one owner, it's data-race-free.
I believe it is fair to state, however, that one of the reasons that Chrome exists is that Google wants Google to be synonymous with the Web in the minds of consumers. When your browser is Google and your search engine is Google, the web to you is Google. This will dovetail quite nicely with all the other places they're trying to expand to once they manage to establish a dominant browser market share.
The author was being unfair to C. Use epoll or, better yet, libuv. libuv is a simple-to-use library.
If WebKit becomes the standard, every user from now until forever will be using the same pile of C++ to browse. That may be fine to you; I personally don't think that WebKit is so good as to make it the gold standard for the Web. Imagine if that had happened with NCSA Mosaic...
Go's memory management story is basically the same as Java's with the "use escape analysis to place objects on the stack" flag enabled. (To be fair, there is one extra thing that Go provides: the ability to allocate objects inside other objects.)
You're only considering the sweep phase. Sweep is the easy part of tracing GC - you can always chuck sweep into a background thread. The mark phase is the problem. Marking always has to trace roots, including stack roots - that's how GC works. Tracing GC never traces dead objects.
You can reduce allocation pressure by using the stack, which will make the GC run less often, but my point is that when it does run you're no better off than Java, and quite a bit worse since Java's GC can run in parallel with the mutator and Go's can't.
"Your comments strongly imply that you have no practical experience with Go's GC. I suggest you stop claiming that it has certain performance characteristics or behaviours when you have not experienced it yourself."
I have experience with GC generally. There's nothing particularly special about Go's GC: it's a standard stop-the-world mark-and-sweep collector for a language that supports a limited form of stack allocation but generally uses heap allocation. The performance characteristics that this form of GC must have are well-known.
If you want data, look at the binary-trees benchmark: http://shootout.alioth.debian.org/u32/benchmark.php?test=all...
It's mostly a test of GC. Java's GC runs more often (thus the memory use is lower) and yet it's still 4x faster. This is because Java has a generational, concurrent-incremental collector.
And a huge amount of graphics processing is always done on the CPU. Unless you're using Direct2D or something, rasterization is always done there (and even if you're using Direct2D, a lot of rasterization is still done there).
Most of the code in any program is not performance critical. Yet the reason that this is the case is that most of the time of any program is spent in tight loops. Those tight loops are what must be optimized. And that is why performance-minded systems programmers use top-notch optimizing compilers.
The popular dynamic languages have gotten away with being relatively slow because programs written in them are either not CPU bound or spend most of their CPU time in C libraries. This works great for them -- dynamic languages are awesome! But the calculus changes when you're programming in a language that defines the entire stack. When there's no C-compiled code to fall back on, micro-optimizations start to matter.
(a) memory you don't have access to to be read;
(b) other threads to crash;
(c) the VM state to be corrupted;
(d) the GC to crash;
and so on. The Java world is still in a consistent state. In a security-oriented world, this is important.
To the reply: It's a bit pedantic for me to say this, I know, but that's still "a consistent state". Basically, a Java program can't do anything it wouldn't otherwise be able to do by modifying a hashmap concurrently. Java programs can go into infinite loops. They can't access uninitialized memory.
Besides, once you have a stop-the-world multithreaded GC, it has to trace all the roots in the program for correctness. There's no way around that. At that point having all the data on the stack doesn't help you (except for cache locality, but Java's GC already does that via the nursery and copying during major collections). You must still trace every pointer in the object graph, while keeping all threads suspended. You can run the GC less often, but that's not much of a help when interactive performance is at stake. iOS feels so great because the UI is always running at 60 frames per second. Stop-the-world GC can't do that.