6 ms·
> Can you name a widely-used contemporary programming language that still uses the 60’s software technology of reference counts to manage storage? As opposed t
by millstone 11y ago
> Can you name a widely-used contemporary programming language that still uses the 60’s software technology of reference counts to manage storage?
As opposed to the 50's software technology of garbage collection?
What an off-putting remark to include.
- deleted 11y ago[deleted]
- TazeTSchnitzel 11y agoYes, age here is irrelevant. At the very least, Objective-C, Swift and PHP still use reference counting.
- amock 11y agoAs does Python.
- SomeCallMeTim 11y agoAnd C++11 just got reference counting as an official library option. Reference counting is better than mark-and-sweep GC for several use cases: * Real time code where you don't ever want a GC to steal cycles. I know that a lot of research has been done to decrease the amount of time stolen, but it's always non-zero. * Immediate, deterministic clean-up of resources as soon as they are no longer referenced: If you have a limited number of file handles, for instance, you want them closed ASAP, and not when some GC decides it's time. * No performance penalty for having weak references. I use this in asset management: A map of asset handles to weak references to currently loaded assets. If an asset is no longer used, it's deallocated immediately. Having weak references in a GC system can increase processing complexity.
- jfim 11y ago> * Real time code where you don't ever want a GC to steal cycles. I know that a lot of research has been done to decrease the amount of time stolen, but it's always non-zero. Real time code shouldn't allocate/deallocate memory, much less from a GC'able pool. With that constraint, it's possible to have real time code that coexists with a GC, such as RTSJ's NoHeapRealtimeThread or Eventrons, with an effective cost of zero cycles taken by GC from the realtime code.
- SomeCallMeTim 11y agoNot talking "hard real time" here, but game code where you want to prevent frame glitches. In C++ you can also replace the allocator to pull items from a pool, so that the "allocation" is "grab the first item from the pool linked list" and "deallocation" is "link this item back into the pool." The first case costs two pointer reads and one write, the second case costs two pointer writes and one read. This lets you use objects as if they're dynamically allocated while still keeping very costs in allocation/deallocation.
- david-given 11y agoImmediate cleanup is not necessarily an advantage. For example, it makes releasing a reference inside a critical section very dangerous, because you have no idea how much, or even what, work is going to be done. (Don't ask me how I know.)
- SomeCallMeTim 11y agoI feel your pain. Critical sections are really designed to wrap only a few lines of code. Basically nothing nontrivial should be done within a critical section, IMO. If you're dealing with multithreading, the only safe thing to do with references is to put them in a list of "things to release later." And then do that from the main thread. GC does make this easier, sure. But creating a "release list" is not hard. Making a GC not stall the program at awkward times is actually a lot harder.
- jkarneges 11y agoReference counting in C++ can be done really well even in the absence of language support. For example, with the Qt library you can pass objects around by value, yet behind the scenes everything is reference counted with automatic copy-on-write. It's the best of all worlds. You get easy, value-based coding (no pointers), speed (because deep down everything is a reference), and deterministic destruction (no GC). http://doc.qt.io/qt-5/implicit-sharing.html http://doc.qt.io/qt-5/implicit-sharing.html I'm curious if any languages have adopted a Qt-style approach natively.
- beagle3 11y agoI know for sure the K (of the J / K / APL) family does, and I suspect they all do.
- ridiculous_fish 11y agoYes, Swift works this way. Arrays, dictionaries, and sets are value types and cannot be implicitly shared, which eliminates a large class of bugs. It's kind of amazing, isn't it!
- aruggirello 11y ago> For example, with the Qt library you can pass objects around by value, yet behind the scenes everything is reference counted with automatic copy-on-write. PHP does exactly this, but for arrays and strings only (objects are implicitly passed by reference). So you can pass arrays by value with no performance penalty, as they are actually passed by reference. A COW mechanism ensures you end up with a local copy only if you write to an argument; such mechanism is disabled when passing arguments byref.
- bjourne 11y agoThis is incorrect due to the existence of reference cycles. You can easily create a cycle of objects not reachable by any of your roots in your object graph. The ref counts won't ever reach 0 so to collect it you still need a gc pass periodically, imposing stop times. For the same reason you must never rely on ref counting to clean up file objects etc.
- vsl 11y agoSorry, but that's nonsense. Cycles don't just magically appear, you write them. So writing refcounting code simply means being aware of this when designing the more complicated data structures in your code to use weak backreferences. File objects are not secretly stashed in complicated graphs to prevent their destruction and you very much can rely on their behavior. GC passes to clean up cycles is something you got confused about: that's what GC does (because unrooted cycles are very much an issue there too!), not refcounting where you always have to break the cycles yourself, manually, preferably when designing the data structures.
- bjourne 11y ago> Cycles don't just magically appear, you write them. That's like saying memory leaks doesn't magically appear, you write them. In real code, ref cycles are everywhere and it is not trivial to know beforehand what code will generate cycles. And don't give the spiel about how that's only something that affects bad programmers.
- comex 11y agoAll modern Objective-C codebases and a high percentage of large C++ codebases use refcounting without a cycle collector. Some of them have memory leaks, and there are some quite ugly cases like [1], but most of them have it under control. So it's not like relying on pure refcounting is impractical in the real world; it's just a bit more difficult to deal with than full GC. [1] http://albertodebortoli.github.io/blog/2013/08/03/objective-c-blocks-caveat http://albertodebortoli.github.io/blog/2013/08/03/objective-...
- kazinator 11y agoReference counting steal cycles all over the place. The counting itself steals cycles, and the effect of dropping a reference is unpredictable: whichever module happens to drop the last reference to an object which is the last gateway to a large graph of objects will suffer a storm of recursive reference count drops and deallocations. If you have a limited number of file handles, you may want them closed ASAP, and not when some reference-counting mechanism or GC decides. Reference counting is not ASAP. Typically, you have some smart pointers which will drop the reference in relation to some lexical scope. That could be too late: the file handle object could be in scope over some long running function, and so the file is held open. The fix is to call the close method on the object, and then let refcounting reap the object later. (Woe to you if you decide to manually take over reference counting and start hand-coding acquire and release calls. Been there, debugged that.) I implemented weak hash tables in a garbage collector; it's neither complicated nor difficult. Under GC, we use weak referencing for essential uses that requires the semantics, not as a crutch for breaking circular references.
- ridiculous_fish 11y agoThe effect of dropping a reference is sometimes predictable. For example, Color cannot root a large object graph, so dropping a reference to Color will deallocate at most one object. At least it does not require nonsense like Android's allocation-in-onDraw warning. I worked on the now-deprecated GC for Cocoa frameworks, and we made heavy use of weak references for out-of-line storage. This put us at risk for cycles: if A references B through a weak-to-strong global map, and B in turn references A, we have an uncollectible cycle even under GC. This represented a large class of bugs we encountered under GC. So both GC and RC have their classes of cycles and unpredictable behavior. I've come to believe that these techniques are more related than we'd like to admit, and the real difference is in their second order effects. For example, GC enables lockless hash tables, which require hazard pointers or other awkward techniques under RC. On the other hand, RC enables cheap copy-on-write, which is how Swift's collections can be value types.
- SomeCallMeTim 11y ago>The counting itself steals cycles An atomic increment/decrement takes so little time as to make this irrelevant. If you're in such a tight loop that you care about a single increment when calling a function (to pass a parameter in), you should have inlined that function and preallocated the memory you're dealing with. I'm talking about general use of smart pointers, which means that there's a function call involved with the smart pointer value copy, and throwing an increment in is trivial by comparison. >whichever module happens to drop the last reference to an object which is the last gateway to a large graph of objects When writing games, I don't think I ever had a "large graph of objects" get dropped at some random time. Typically when you drop a "large graph" it's because you're clearing an entire game level, for instance. Glitches aren't as important when the user is just watching a progress bar. And you can still apply "ownership semantics" on graphs like that, so that the world graph logically "owns" the objects, and when the world graph releases the object, it does so by placing it on the "to be cleared" list instead of just nulling the reference. Then in the rare case where something is holding a reference to the object, it won't just crash when it tries to do something with it. In this rare case a release could trigger a surprise extra deallocation chain, as you've suggested. If that's ever determined to be an issue (via profiling!) you can ensure other objects hold weak references to each other (which is safer anyway), in which case only the main graph is ever in danger of releasing objects -- and it can queue up the releases and time-box how many it does per frame. Honestly having objects reference each other isn't typically the best answer anyway; having object listeners and broadcast channels and similar is much better, in which case you define the semantics of a "listener" to always use a weak reference, and every time you broadcast on that channel you cull any dead listeners. Aside from all of that, if you're using object pools, you'd need to deallocate thousands, maybe tens of thousands, of objects in order for it to take enough time to glitch a frame. Meaning that in typical game usage you pretty much never see that. A huge streaming world might hit those thresholds, but a huge streaming world has a whole lot of interesting challenges to be overcome -- and would likely thrash a GC-based system pretty badly.
- comex 11y agoAs does Rust, though it prefers unique ownership.
- steveklabnik 11y agoTo be clear, Rust offers a reference-counted type in the standard library, it's not part of the language itself.
- MichaelGG 11y agoDon Syme, creator of the F# language, wrote to an OCaml mailing list about how hard it was to get generics into the CLR. How MSCorp viewed it as "academic" and "experimental". Despite that what he was doing was basically implementing ideas from the 70s into a modern system. It's strange how anyone would look back and think the language ideas from back then are all useless. Or that there's significantly new stuff going on now in mainstream implementations.
- WalterGR 11y agoDon Syme, creator of the F# language, wrote to an OCaml mailing list about how hard it was to get generics into the CLR. How MSCorp viewed it as "academic" and "experimental". Link?
- MichaelGG 11y agohttp://caml.inria.fr/pub/ml-archives/caml-list/2006/11/d921ca59f12d434a3f005dfd07256d91.en.html http://caml.inria.fr/pub/ml-archives/caml-list/2006/11/d921c... Don Syme's blog has some more details on how deeply MSR got involved to ship generics. MS Corp simply wasn't capable or interested. This gets revised when talking to folks today - claims that oh yeah, Delphi++ was always gonna do generics right. But in truth, it seems they weren't going to make it without MSR's (F# team overlaps with the generics team) help. http://blogs.msdn.com/b/dsyme/archive/2011/03/15/net-c-generics-history-some-photos-from-feb-1999.aspx http://blogs.msdn.com/b/dsyme/archive/2011/03/15/net-c-gener... It's incredibly frustrating to see, even as an outsider. Having the patience, tact, and political savvy to pull this off? It's pretty impressive. But did MS learn? Nope. F# is still a second-class citizen, with only token support. F# still isn't marketed as "generally better than C#", but still aimed at "niche" users. It's sad. MS language tech is pretty much stagnated for 8 years now. Feels like IE all over again. They trounced their major competitor (Java), so now they can kick back and add minor stuff here and there because there's no pressure.
- WalterGR 11y agoInteresting, thanks. I didn't understand how Don Syme needed to convince Microsoft since he is part of Microsoft. From the blog: > Generics for .NET and C# in their current form almost didn't happen: it was a very close call, and the feature almost didn't make the cut for Whidbey (Visual Studio 2005). Features such as running CLR code on the database were given higher priority. > ... > Ultimately, an erasure model of generics would have been adopted, as for Java, since the CLR team would never have pursued a in-the-VM generics design without external help. I have no particular reason to doubt his perspective - and I certainly don't have any inside knowledge - but a feature not making the cut for version N doesn't always imply that a shitty version would have been implemented in version N+1. > The world is as it is today because we act when we have the chance to act, and because we invest in foundations that last the test of time. Could this be a case of the victors getting to write the canonical history? MS language tech is pretty much stagnated for 8 years now. Feels like IE all over again. Nah. Sure, C# 6.0 is largely smaller tweaks here and there, but by all measures the team has been busy with Rosyln, which is huge. Out of curiosity, are there language features that you think should be added to C#? For C# to be like IE, the world would need to have moved on and C# would have to be behind in this new world. It doesn't seem like that's the case. And compared to Java, C# moves at a pace that's absolutely breakneck. What has Java gotten in the pat 8 years? Crappy not-closures? They trounced their major competitor (Java) Microsoft may have trounced Java-on-Windows, but overall Java is more popular than C# by a significant margin.
- kazinator 11y agoA time-sharing multi-user system with demand-paged virtual memory is also 60's technology.