7 ms·
I don't see your point. Of course sometimes the lifetime of an object is not tied to code scope but actually to something dynamic. Let's say for instance when y
by simias 8y ago
I don't see your point. Of course sometimes the lifetime of an object is not tied to code scope but actually to something dynamic. Let's say for instance when you close a tab in your browser you expect the resources to be freed (ignoring caching to simplify the argument).
Clearly somewhere in your code you have to explicitly handle tab closing and break the references to allow the GC to do its job. Why not free the resources here while you're at it?
When you say "manual memory management" if you're thinking C-style malloc-free then you have a point, it's very easy to forget a free() somewhere. But any language with destructors can handle these situations without much more overhead than GC-based approaches. Just remove your object from whatever data structure was holding it and let the language automagically run the destructor for you. I find RAII a lot easier to model and think about than "you drop this reference and one day maybe your object is destroyed, but don't really rely on that".
>No one has ever devised a scheme that lets you specify very dynamic lifetimes AND statically checks that no leak can occur.
GC doesn't solve that either. If you're not careful and let a reference to old data dangle somewhere in your code you can effectively "leak" data just fine. When there's no clear ownership and nobody knows who's supposed to clean what it's very easy to end up with data dangling everywhere through complex ownership graphs. GC may mitigate some use-after-free problems (generally by masking the problem) but that is something that can be solved without GC either.
I'm not saying that applications with (justifiably) extremely complicated ownership semantics that also require very high performance don't potentially benefit from using a GC and that it could outperform manual memory management by, for instance, batching the object deletions. What I'm saying is that it's that such applications are few and far in-between, basically 100% of all the code I've ever wrote in my ~15years as a professional software developer didn't have such requirements.
GCs should be an opt-in niche tool used to solve specific problems, not a kludge to let developers write sloppy code and get away with it.
- lossolo 8y ago> Clearly somewhere in your code you have to explicitly handle tab closing and break the references to allow the GC to do its job. Why not free the resources here while you're at it? You are missing the point. The main point behind GC is removing the complexity of writing the software. Writing your own destructors, thinking about when to free your memory or writing lifetime annotations, needing to design your app in specific way to satisfy the borrow checker etc in languages like Ada/C/C++/Rust all are additional complexity and mental overhead that is removed by GC. Your point about "just do this X and you don't need to use GC" is a reason people use GC languages, because they don't want to think about or write this "X".
- irq11 8y agoHaving spent years writing code in both GC’ed and non-GC’ed languages, it’s pretty clear that it programmers spend about the same amount of time worrying about memory management with either paradigm. The main differences are that people using GC systems spend their time doing magical incantations to manage the GC wizard, and they’re a lot more smug about the goodness of their system.
- cblades 8y ago>programmers spend about the same amount of time worrying about memory management with either paradigm I have no idea how you've come to that conclusion. For the vast majority of memory allocation in any GC'd language I can think of, you spend zero time thinking about memory management 98% of the time. whereas in a manual language, you must consider it every time you allocate memory. That's not a bad thing, it's often dead simple in the vast majority of contexts, but still. I've also very rarely seen code in professional code that's mean to poke or prod the GC into running.
- dleslie 8y agoFor 95% of the code you write it's not a concern; that last 5% sure can eat up time if your software has any concern about performance.
- cblades 8y agoTotally agree. I don't think that tradeoff is particularly out-balanced by the time it takes to do manual memory management.
- simias 8y ago>For the vast majority of memory allocation in any GC'd language I can think of, you spend zero time thinking about memory management 98% of the time. >whereas in a manual language, you must consider it every time you allocate memory. This is like bizarro world to me. I genuinely can't comprehend that. Here's how memory management looks like to me, using concrete examples in languages featuring RAII like C++ or Rust: - I have a function that needs to compute a hash so I need to allocate a hash context for it. If it's small I'll put it on the stack, otherwise it'll go on the heap. In either case when the function returns the destructor is automatically called. - I have a function that takes the name of a file as parameter and returns its contents in a buffer. Clearly the buffer needs to be allocated on the heap, so I allocate a large enough vector or string read into it and return that. The caller will then either use it and drop it immediately or store it somewhere to be dropped later, either on its own or as part of the structure it belongs to. In either case the destructor will be called automatically and the memory will be freed. That's easily 99.9% of what memory management looks like in the programs that I write. I have absolutely no idea what using a GC would change at any point here. Note that while this is technically "manual" memory management I never have to explicitly free anything and in the vast majority of cases I don't even have to bother implementing the destructor myself (I basically only need to implement them if I need to release external resources like, for instance, raw OpenGL handles, file descriptors and the like. The GC wouldn't help either here). It's genuinely a non-issue as far as I'm concerned. I literally never think "uh, I have no idea when this object won't be used anymore, I wish I had a GC to figure it out". I can't even imagine when such a scenario would crop up.
- norswap 8y agoI mostly agree with your point on RAII. But I don't think OP was advocating RAII. The typical argument for GC is that it is less error-prone than even RAII. And I agree with that point. It's a trade-off on which you have to decide on. I think the tiny perf penalty is often worth it. You can still retain stale data under a GC, but real leaks (unreachable allocated memory) are precluded — which is usually considered (not unreasonably) to be the more common and serious problem. I think GC is a sane default because it's less error prone, and god knows the average developer will make enough errors as it is. I'd trust myself with manual memory management, but not a random group of developers I don't know. What about you?
- nine_k 8y agoYou can of course introduce a space leak in a honest GC environment. Make a linked list, and keep adding items to it while keeping its head referenced. Of course you do it by mistake: you kept two references, the main and the auxiliary, and the main reference is duly gone. This is why weak references were invented.
- c-smile 8y ago> GCs should be an opt-in niche tool used to solve specific problems. That is true. Sciter (https://sciter.com https://sciter.com) contains implementation of DOM(tree), CSS and script. DOM is a regular tree structure - each element of the tree has strictly one parent and no cycles on the tree in principle. It does not need GC at all and is not using it for DOM tree management. CSS is a collection of collections of name/value pairs. No loops. It does not need GC at all. And only script uses GC for the simple reason: in UI, ownership graph of script objects frequently contains loops. Yet lifespan of objects is unknown at compile time - depends on user interactions. Even purely native GUI application benefits from GC when that GC is used in places where it is needed. GC is just one way (of many) of doing memory management. Ideally it should be a multi-paradigm programming language and environment that allows to use as explicit memory management (and so RAII, memory pools, etc) as a GC. int *ptr = …; gcable int* gptr = …; But unfortunately such dualism is hard to achieve.
- kazinator 8y agoRather, GC should be default, and you opt-out of GC for specific optimization problems.
- skrimp 8y agoThis is very hard to achieve because GC + RAII do not play nice together. It's hard to reason about whether an RAII resource has been cleaned up since managed objects have non-deterministic destruction. This non-determinism is unsuitable for common RAII patterns like scoped mutex locks. Now, the question being begged is "Can this be avoided by not using destructors with managed objects?" Yes, but not reasonably. Any unmanaged objects that are owned in the entire managed object graph now also have non-deterministic destruction. Thus, the only way to reasonably follow this constraint is to have the compiler disallow/warn when an object with a non-trivial destructor (explicit or implicit) is used with GC. If that wasn't enough of a pain, any runtime-polymorphic types may also need to be destructed and these can't be known at compile time if dynamic linking is used. In essence, these concepts are incompatible to a point where any language supporting both will likely make extensive trade-offs. I find this really sad because many developers may never be exposed to how elegant RAII can be if they are never exposed to non-memory-managed languages. Sure it can be approximated using `with` constructs, but those don't allow resource allocations to cross scope boundaries.
- rayiner 8y ago> When you say "manual memory management" if you're thinking C-style malloc-free then you have a point, it's very easy to forget a free() somewhere. But any language with destructors can handle these situations without much more overhead than GC-based approaches. What happens when that object is shared and still referenced from somewhere else? Now you have a use after free error waiting to happen.
- tomp 8y ago> Let's say for instance when you close a tab in your browser you expect the resources to be freed That's basically region-based memory management. Which is a great idea, but unfortunately only applicable in narrow scenarios (another example besides browser tabs is user requests in a HTTP server). The real issue, however, is what if you can't keep all current task's data in RAM! E.g. browser tabs still have GC for JavaScript... Going a bit meta, you can easily think of a hierarch of such "regions" (e.g. computer > process > browser tab), each is self-contained in the sense that when you turn it off/terminate it/close it all its resources are reclaimed, but the real issue is when they need more resources than you have... Then you're back at needing GC. I agree with your second point; GC doesn't solve the problem of reference leaks... Whereas regions mostly do! So maybe more language support for regions (finer-grained than "whole process" - think Erlang) would be useful (each region can still of course optionally have its own GC).
- kazinator 8y ago> Why not free the resources here while you're at it? Because you do not have an exclusive reference to all of them; we could be freeing something that is still in use somewhere. So why not reference-count? Because reference counting is slow, and must be meticulously maintained (here, languages help with constructs like smart pointers), and doesn't handle cycles in the object structure. > I find RAII a lot easier to model and think about than "you drop this reference and one day maybe your object is destroyed, but don't really rely on that". The RAII model doesn't do away with this "maybe" in any shape or form. We drop this reference, and the smart pointer decrements the refcount; maybe it has hit zero so the object is destroyed, maybe not. Maybe the zero refcount triggers a whole cascade of thousands of other objects hitting their zero refcount, which takes many cycles to execute, or maybe it doesn't.
- Shikadi 8y agoRAII has nothing to do with reference counting, it stands for resource acquisition is initialization. In C++ that means the lifetime of a resource is tied to the creation and destruction of an object. Often when people talk about RAII and C++ they are also referring to destructors being called when an object goes out of scope, so if an object that contains an object containing some allocated resource goes out of scope, the resource is released as the destructor path is called. If an object with a smart pointer goes out of scope, then of course there's the overhead of the smart pointer, but the previous comment wasn't referring to that situation
- kazinator 8y agoRAII is commonly used for representing smart pointers that manage references to objects that themselves have otherwise pervasive lifetimes. RAII not managing any shared resources is trivial and uninteresting. The lifecycle of an object that is confined to a single lexical scope can be handled manually and verified by visual inspection (though of course it is more reliable to automate that with RAII or macros, not to mention less verbose).
- SilasX 8y ago>I find RAII a lot easier to model and think about than "you drop this reference and one day maybe your object is destroyed, but don't really rely on that" Heh, I just realized this is what we depend on to remove commits from Github. "Uh, just delete any branches that contain it, and, uh, it'll get removed ... like, eventually." (of course you have to assume any secret data is compromised)