What do you mean by “Memory Management”?
deusinmachina.net
deusinmachina.net
Unfortunately, the * in the *pointer_my_var syntax is pulling double duty here as it is used both to dereference a pointer as well as declare a pointer.
This isn't unfortunate: It's a rule in C that declaration should mirror use, that a variable's declaration should look like that variable's use. This is more obvious when declaring function pointers: int (*cmp)(char *, char *);
which declares cmp as a pointer to a function which takes two pointers-to-char and returns an int, and obviously cmp is used like: res = cmp(foo, bar);
What's unfortunate is how it's impossible to escape asterisks in normal text around here.Also: Don't cast the return values of malloc and friends. It can mask a failure to include stdlib.h, which means the compiler considers the functions to return an int, which is half the size of a pointer on most modern systems, so your pointer just got mangled. Yes, implicit int is Officially Bad in the modern standard. How much do you want to bet your compiler is strict enough to catch this?
You can escape an asterisk in normal text by putting a backslash in front of it: int (*cmp)(char *, char *);
Described here: https://news.ycombinator.com/formatdoc (which is linked from the HN FAQ)
This form of variable-allocation is now done automatically (ex: int foobar; will automatically manage a memory location to be representing a 4-byte value named foobar). This is the simplest way of managing memory (even on extremely constrained modern platforms like ATTiny4, which only has 256 Bytes of RAM. BTW: I do suggest people play with microcontrollers, if only to improve your skills at programming in constrained environments).
But as you start having to efficiently manage more than that: a million bytes (aka: 1MB), or a billion bytes (aka: 1GB), more complex patterns arise. In particular, a program may not know how much memory it needs at compile time, and may need to shift the amount of memory usage arbitrarily upon the user's work pattern.
For example: lets say you are writing a web-client, and your user is going to visit the webpage www.google.com. How much memory should you allocate for it? Well, you don't know. You have to start downloading the page before you even know how much memory it will use. (Upon talking to www.google.com, the Google Server says in the HTTP protocol how many bytes the webpage will take up, giving the program an opportunity to allocate memory at that point). To properly store the webpage, you need a memory-management scheme.
-------------
Modern languages have two patterns for more complex memory management like the "browse to www.google.com" example I described above.
1. C's malloc-and-free pattern. The programmer reserves memory with malloc or calloc. And the programmer says that they're done using the memory with free(), allowing a future malloc() call to recycle the memory.
2. Garbage collection, aka malloc-only. The system automatically determines when memory "should be freed". Though more complex to program, its been popular even as early as the 1970s. It turns out that programmers are pretty bad at writing free() correctly in practice.
There's also the "automatic" memory management implemented by the compiler. This is called "stack allocation" in most cases, and IMO is important to understand how it works... if only because of how efficiently it recycles the L1 Cache and speeds up your program. But we can talk about that some other time!
I count four, even if you discount pure static pre-allocation:
1. Manual dynamic memory allocation and deallocation, as with malloc() and free() and, in C++, new and delete, possibly augmented with things like RAII which don't fundamentally change it.
1.5. Reference counting lives somewhere in here, between pure manual allocation/deallocation and true garbage collection, in that it automates some aspects of resource management but needs some extra manual help to deal with circular data structures that real garbage collection has no problem with.
2. Garbage collection, in multiple different algorithms and optimizations.
3. Linear types, which enable static lifetime analysis, as is done in Rust.
4. Purely declarative languages and other extremely high-level languages that effectively take it out of the programmer's hands entirely and handle everything implicitly.
Borrow Checker is... hmm... I guess that's a new-ish pattern. Its inspired from C++ unique_ptr and auto_ptr (now obsolete, use unique_ptr instead). I'd say that Borrow Checker is still manual memory management, except the compiler writes the free() for you.
#4: purely declarative languages and such is going to be garbage collected in practice.
----------
So 1.5, 2, and 4 are all garbage collection.
#3 / Rust's borrow checker is debatable. I'm still inclined to call that manual malloc/free(), even if the compiler handles the free() for you. Because the compiler can only free() if you code it in the exact way that Rust (or C++ unique_ptr) expects the memory to be used.
https://rushter.com/blog/python-garbage-collector/
However:
> I'd say that Borrow Checker is still manual memory management, except the compiler writes the free() for you.
And:
> #4: purely declarative languages and such is going to be garbage collected in practice.
My point is to catalog the different ways programmers have to think about resource usage, not implementation details. Python is a GC'd language, and unlike C++ with smart pointers, because it doesn't make the programmer manually manage memory in order to avoid a leak, and regular expressions and SQL are high-level in that the languages don't even offer a way to express the concept of resource management.
Splitting the difference between what? Both reference counting and generational garbage collection algorithms are garbage collection algorithms. The generational garbage collection algorithm is to handle issues with things like cyclic references (A->B, B->A) which would cause a pure reference counting garbage collector to retain the memory even if nothing else referenced A or B.
The borrow checker does not determine when to call free. It also doesn't call malloc. It is completely orthogonal to heap allocation.
All it did was cover some C standard library functions and say that what a GC does is manage the memory lifecycle in languages like Python.
What did other people take from this?
It covers pointers/addresses, what they are, the notation for declaring them, finding the address of variables/functions, how they're represented, and how to dereference them. Then the stack and the heap, and how memory is allocated/freed on both.
It's a primer on the important concepts, if you've only ever worked with languages that abstract those details away (e.g. python/javascript).
And, giving it a bit of a skim, it looks like it does an OK job of that. (But I'm not a beginner there, so what makes sense to me might be taking overly large steps for actual beginners in some places.)
¯\_(ツ)_/¯
I don't understand that implication that this is somehow not enough to warrant existing in writing (i.e. that there is "no point").
I have talked to full stack devs that don’t know what the stack and heap is, they think I am talking about leetcode data structures.
This is a beginner article for sure, but I think that’s the point
For python, I believe a correct use of mmap for large files (ex: pickles) could offer a lot of advantages.
This was seen recently with the orders-of-magnitude increase in performance for the several hundreds of GB worth of weights, it might extend to other languages.