I wonder if frameworks like dotnet or JVM will introduce reference counting as a way to lower the RAM footprint?
I wonder if frameworks like dotnet or JVM will introduce reference counting as a way to lower the RAM footprint?
A lifetime system could possibly eliminate those, but it'd be hard to add to the JVM at this point. The JVM sort of has it in terms of escape analysis, but that's notoriously easy to defeat with pretty typical java code.
Swift routinely optimizes out reference count traffic.
It won't always require it, but it usually will because you have to ensure the memory containing the reference count is correctly set before handing off a pointer to the item. This has to be done almost first thing in the construction of the item.
It's not impossible that a smart compiler could see and remove that initialization and destruction if it can determine that the item never escapes the current scope. But if it does escape it by, for example, being added to a list or returned from a function, then those two atomic writes are required.
This has negligible overhead in most cases. For instance, if the shared counter is already in some cache memory the overhead is smaller than a normal non-atomic access to the main memory. The intrinsic overhead of an atomic instruction is typically about the same as that of a simple memory access to data that is stored in the L3 cache memory, e.g. of the order of 10 nanoseconds at most.
Moreover, many memory allocators use separate per-core memory heaps, so they avoid any accesses to shared memory that need atomic instructions or locking, except in the rare occasions when they interact with the operating system.
This is such a problem that the JVM gives threads their own allocation pools to write to before flushing back to the main heap. All to reduce the number of atomic writes to the pointer tracking memory in the heap.
It makes more sense for application developers to think about the unnecessary complexity that they add to software.
Go also does M&S and yet uses less memory. Why? Because go isn't compacting, it's instead calling malloc and free based on the results of each GC. This means that go has slower allocation and a bigger risk of memory fragmentation, but also it keeps the go memory usage reduced compared to the JVM.
The JVM uses a lot of memory a) because it's tuned for servers and not for low memory usage and b) because Java is a poorly designed language without value types.
No, it reduces memory fragmentation, which is why it's called compacting and not compression.
I do agree that the lack of value types is a big contributor to why Java uses so much memory. But it's not a server tuning thing that makes the JVM lean memory heavy.
The JVM uses moving collectors and that is the big reason why it prefers having so much memory available. Requesting and freeing memory blocks from the OS is an expensive operation which the JVM avoids by grabbing very large blocks of memory all at once. If you have a JVM with 75% old gen and 25% new gen, half that new gen will always be empty because the JVM during collection moves live data from one side of the new gen to the next. And while it does that, it slowly fills up old gen with data.
Even more modern collectors like G1 prefer a large set of empty space because it's moving portions of old gen to empty regions while it does young collection.
As I mentioned, the difference here between the JVM and python or go is that python and go do no moving. They rely heavily on the malloc implementation to handle grabbing right sized blocks from the OS and combating memory fragmentation. But, because they aren't doing any sort of moving, they can get away with having more "right sized" heaps.
…which reduces memory usage because you don't have to waste it on free holes in the allocated pages.
It's an allocation and cache optimization more than a memory saving optimization.