4 ms·
How are you getting "constant memory leaks" in a GC'd language?
by 0x696C6961 1y ago
How are you getting "constant memory leaks" in a GC'd language?
- sim7c00 1y agoit's not hard. GC lets shit leak until it decided to clean it up... do you think they will enable Valgrind if there's no leaks?
- chrsig 1y agovalgrind finds sooooo many more problems than just memory leaks uninitialized memory, illegal writes, etc... There's a lot of good stuff that could be discovered.
- sim7c00 1y agonot to mention cachegrind, callgrind and other things it bundles. sorry, i guess when i say leaks i mean a bit more broad stuff :'). my own words are a bit leaky hah still doesnt mean i am wrong. GC doesnt clean up memory when its released but when it wants to, effectively offering opportunities to get that data after a program dont need it anymore. until some point in time u can usually not specify, just hint at. that in light of things like bad memory ordering between threads etc..can have nasty bugs... (raii has similar bugs but since its more determenistic you can program your way around lot of it more easily and reliably)
- johncolanduoni 1y agoGolang has a feature that I love in general but that makes it very easy to keep unintended allocations around. If you have a struct with a simple int field, and you store that somewhere as an *int, the entire struct and anything it points to will be kept alive. This is super useful for short-lived pointers, and super dangerous for long-lived pointers. Most other widely used GCed languages don’t allow the use of arbitrary interior pointers (though most GCs can actually handle them at the register level).
- aleksi 1y ago> If you have a struct with a simple int field, and you store that somewhere as an *int, the entire struct and anything it points to will be kept alive. While Go allows interior pointers, I don't think what you say is true. runtime.KeepAlive was added exactly to prevent GC from collecting the struct when only a field pointer is stored. Take a look at this blog post, for example: https://victoriametrics.com/blog/go-runtime-finalizer-keepalive/ https://victoriametrics.com/blog/go-runtime-finalizer-keepal...
- johncolanduoni 1y agoI don’t believe that’s the case based on the example in the blog post. The fd field in that struct was passed into the later function by value (i.e. as an int, not an *int), so there was no interior pointer in play at all.
- aleksi 1y agoYou are right; I stand corrected
- deleted 1y ago[deleted]
- dijit 1y agoThere’s ways, GC isn’t perfect. A common one I see fairly often is opening a big file, creating a “new slice” on a subset of the file and then using the “new slice” and expecting the old large object to be dropped. Except, the “new slice” is just a reference into the larger slice, so its never marked unused.
- giancarlostoro 1y agoInteresting, I always thought of slices as stand-alone, I wonder if its the same in Python?
- porridgeraisin 1y agoNo, python slice syntax on lists returns a new list. Of course, the slice syntax on your own classes can do just about anything using operator overloading.
- gdbsjjdn 1y agoA go slice is a wrapper around a normal array. When you take sub-slices those also point to the original array. There's a possible optimization to avoid this footgun where they could reallocate a smaller array if only subslices are reachable (similar to how they reallocate if a slice grows beyond the size of the underlying array).
- kbolino 1y agoMost sublicing is just the 2-arg kind so it would not be safe to truncate the allocation even if the subslice is the only living slice because the capacity still allows indirect access to the trailing elements of the original slice. This optimization would only be truly safe for strings (which have no capacity) or the much less common 3-arg slicing (and Go would need to have a compacting GC). Of course, the language could also be changed to make 2-arg slice operations trim the capacity by default, which might not be a bad idea anyway.
- gdbsjjdn 1y ago
- throwaw12 1y agothere are many ways: - you can create deadlocks - spawn goroutines while not making sure they have proper exit criteria - use slices of large objects in memory and pass them around (e.g. read files in a loop and pass only slice from whole buffer) - and so on
- deleted 1y ago[deleted]
- GuB-42 1y agoA GC only deallocates unreferenced memory, if you keep unused references, that's a leak the GC won't catch, as it has no way to know that you won't need it later. It can happen when your variables have too long a lifespan, or when you have a cache where the entries are not properly evicted.
- foldr 1y agoBut how would Valgrind know more than the GC? Of course a program in a GCed language can leak memory, but it’s not clear to me how Valgrind would detect the kinds of memory leaks that you can create in pure Go code (without calling into C or using unsafe functions to allocate memory directly).
- GuB-42 1y agoValgrind can tell you what is still in use when the program exits, along with useful information, like where it comes from. You can then assess to situation and see if it is normal or not. In addition, Valgrind is actually a complete toolsuite, not just a memory leak detector. Among these tools is "massif", a memory profiler, so you will have a graph of memory use over time and it can tell you from where these allocations come from. Not, if your language is fully GCed you can have a debug GC that does the job more efficiently than Valgrind on this task, but I don't know if it is the case for Go.
- foldr 1y agoPprof should cover most of that for pure Go code, though.
- BlackFly 1y agoThis was often a question asked in Java interviews as well. In Java heap fragmentation is usually considered a separate issue but I understand go has a non-moving garbage collector so you can lose memory due to pathological allocations that overly fragment memory and require constantly allocating new pages. I could be wrong about this since I don't know a lot about go, but heap fragmentation can cause troubles for long running programs with certain types of memory allocation. Beside that, applications can leak memory by stuffing things into a collection (map or list) and then not cleaning it up despite becoming "stale". The references are live from the perspective of the garbage collector but are dead from the application perspective. Weak references exist to solve this problem when you expose an API that stores something but won't be able to know when something goes out of scope. I wouldn't consider this to be common, but if you are building a framework or any kind of platform code you might need to reach for this at some point. Some crazy folks also intern every string they encounter "for performance reasons" and that can obviously lead to what less crazy folk would consider a memory leak. Other folk stick a cache around every client and might not tune the cache parameters leading to unnecessary memory pressure...
- lukaslalinsky 1y agoNot going to claim this is all sources, but Go makes it extremely easy to leak goroutines.