4 ms·
Never calling free() is a valid memory management strategy if you know your process lifetime exactly.
by pavlov 2y ago
Never calling free() is a valid memory management strategy if you know your process lifetime exactly.
- helpfulContrib 2y ago[dead]
- Hamuko 2y agoOldie but a goodie: https://devblogs.microsoft.com/oldnewthing/20180228-00/?p=98125 https://devblogs.microsoft.com/oldnewthing/20180228-00/?p=98...
- alternatex 2y agoI think the complaint is that there was no strategy. The dude just wanted to build some websites.
- rwmj 2y agoI tried to do this in a real program once. The program only ever runs for a fraction of a second and then exits, so it seemed like a good candidate. Unfortunately what happened after is that our company started running tools like Coverity which complained about leaked memory. The path of least resistance was to fix that by adding free()'s everywhere.
- matja 2y agoAnd then you accidently add a use-after-free bug by trying to satisfy a "correctness" tool...
- SubjectToChange 2y agoIt’s hard to fault the static analysis tooling here. Not freeing memory is, almost always, unintended behavior. Besides, if a business is going to blindly apply static analysis to their projects and then demand that every warning be “fixed”, then the tool really isn’t the problem. Still, any serious developer should, at the very least, have the patience to interrogate their code.
- nkrisc 2y agoThe tool doesn’t demand it be satisfied, a manager does.
- jmb99 2y agoCoverity will definitely warn you about use-after-free. It’s not a “correctness” tool, it’s a static analyzer and probably the best one out there (imo). Yes in this use case it’s probably not too important to care about, but really any code base of importance should be run through it on a fairly regular basis.
- adrianN 2y agoHow much use after free and double free did you have to fix?
- samatman 2y agoWorth pointing out that in Zig, you wouldn't have had this problem in the first place. The natural way to write a program of that nature is to create an arena allocator, immediately defer arena.deinit() on the next line, and then allocate to your heart's content. When the function returns, on an error or otherwise, the arena is freed. No need to go back later and add a bunch of free(), because you correctly implemented the memory policy as a matter of course, in two lines.
- frankjr 2y agoThat has nothing to do with a particular language, you could use individual allocations in Zig and end up with the same mess. Similarly, you could use memory arenas in C/... and not have the problem.
- samatman 2y agoDefaults matter. In C, allocation is customarily done with malloc, a single, global allocator. It's possible to bring in a memory arena, but in the case I was replying to, you'll note that they didn't. In Zig, everything which allocates receives an allocator to do so. So writing a top-to-bottom program of the sort indicated in the GP post, you're going to make an allocator first thing. That's when you decide to use an arena, and defer it, and you're done. You wouldn't decide to use malloc (which is available), or the GeneralPurposeAllocator, and then "end up with the same mess" by forgetting to release any of the memory, because the debug release mode, where all the work gets done, will loudly complain about that. It would take real stubbornness and conviction to write an all-malloc-no-free program in Zig, but using an arena is cheap and easy. So yes, it has everything to do with a particular language. If you wrote it in Rust, you'd end up with a borrow-checker-compatible program, if you wrote it in Nim, allocation would be taken care of by the runtime. In C, it's tempting to just use malloc and let the operating system do the GC when the process exits.
- iveqy 2y agoGit is actually using that approach which means that libgit is pretty useless to embed, which noone does anyway since it's GPL and everyone instead uses libgit2.
- mhh__ 2y agoDon't do it though. Unless you know you will only allocate 100x times less memory than the average user has it will bite you. 1. Your code is now fragile (in the taleb sense). 2. Your code is now unusable when someone wants to use it as a library in the future 3. You are now prone to memory fragmentation (many use a bump the pointer allocator when not freeing). 4. You encourage people to not care about free-ing anything — when you do turn free on, or turn a GC on, you might struggle to actually free anything because of references all over the place.
- MaxBarraclough 2y agoIn certain circumstances it can be a good move. It might significantly improve real world performance, for instance. Walter did this in the DMD compiler years ago and it gave a drastic performance boost. [0] > You are now prone to memory fragmentation (many use a bump the pointer allocator when not freeing) Pointer-bump allocation is immune to fragmentation by definition, no? Keeping dead objects around might lead to caches filled with mostly 'dead' data though. [0] https://web.archive.org/web/20190126213344/https://www.drdobbs.com/cpp/increasing-compiler-speed-by-over-75/240158941 https://web.archive.org/web/20190126213344/https://www.drdob...
- mhh__ 2y agoWalter doing that is why compiling my work project uses 70 gigs of ram and is slow - because nothing stays in the cache because everything gets copied so much (because copies are cheap right?) Anecdotally people have told me that's it's faster with a modern malloc impl anyway, haven't tried it properly. > Pointer-bump allocation is immune to fragmentation by definition, no? In some sense yes but what I mean is that like should end up near like whereas if you have everything going through a single bumping allocator then you can have a pattern of ABABAB where a proper allocator would do AAABBB which the cache can actually use.
- MaxBarraclough 2y ago> Walter doing that is why compiling my work project uses 70 gigs of ram and is slow - because nothing stays in the cache This sounds like guesswork. Do you know the details of DMD's internals? Have you done profiling to confirm poor cache behaviour? Again, per the article I linked, when Walter made the change there was a drastic improvement in performance. > everything gets copied so much (because copies are cheap right?) I'm not sure what copying you're referring to here. Declining to call free has nothing to do with needless copy operations. > like should end up near like whereas if you have everything going through a single bumping allocator then you can have a pattern of ABABAB where a proper allocator would do AAABBB which the cache can actually use. A general purpose allocator like malloc can't segment the allocations for different types/purposes, it only has the allocation size to go on. That's true whether or not you ever call free. If you want to manage memory using purpose-specific pools, you'd need to do that yourself. As for whether this really would improve cache performance, I imagine it's possible, which is why we have discussions about structure-of-arrays vs array-of-structures, and the entity component system pattern used in gamedev. I'd be surprised if compiler code could be significantly accelerated with that kind of reworking, though.
- leni536 2y agofree() doing nothing can be a valid application-level strategy. Not calling free() can bite you down the line.
- lanstin 2y agoCGI. But once you loose control of your memory in a large code base it is practically impossible to regain it. So you can’t move to long lived server processes when you decide to enter the more modern era.