After that lengthy discussion in comments, I think some summary answer makes actually sense to this question (also I think the question is NOT that bad - to deserve downvotes, after all you are asking about particular programming concept, just misunderstanding it a bit, probably that annoys people, but for me it looks like question about programming).
First thing, the OS keeps track of used/free memory in its internal structures, storing rather things like pointers/ranges of addresses, working with "pages" of memory rather than single bytes. So the actual content of memory is of no interest of OS, if memory at physical addresses range 0x10000-0x1FFFF is being tracked in OS internal data as "free", it's free. The content of bytes doesn't matter. If that memory area is claimed by some process, the OS does track that in its internal data, so upon termination of that process it will mark that area as "free", even if the process explicitly didn't manage to release it before termination.
Actually OS usually does not clear memory upon allocation request, due to performance reasons (although I *guess* some security-hardened OS may actually clear RAM after every terminated process, just to make sure nothing malicious or sensitive will leak in future to next process reusing the same physical memory). If the app was programmed in language which guarantees the newly allocated memory is cleared, that that's the responsibility of that language runtime to provide that.
For example C and C++ does not guarantee zeroed memory (again performance reasons, clearing takes time), but they have the heap-memory manager code in the libc runtime code, added to every application compiled from C sources and using default libraries and runtime. The heap manager allocates free memory from OS in bigger chunks, and then it micro-manages it for the user code, supporting new/delete/malloc/free
, which actually don't go directly to OS memory manager, that's what the internal C runtime will do when it will exhaust its current pool of available memory.
So there's no need to zero values to reclaim the memory for OS, it has to "zero" only its internal data about which parts of RAM are being used and by which process.
This OS memory manager code is probably not trivial (I never bothered to check actual implementation, but if you are really into it, get some book about OS architecture, and you may also study sources of current operating systems), but I guess in principle upon booting up it maps the available physical memory, cuts it into different areas (some are off-limit to user code, some ranges are memory-mapped I/O devices, so they may be off-limit to everyone except the particular driver of the device, and usually the biggest chunk is "free" memory for user applications), and keeps something like list of available memory "pages" or at whatever granularity the OS wants to manage it.
So who cleans up RAM (and other resources) - the OS, upon terminating certain process, and good OS should be designed in such way, that it will be capable to detect all blocked resources by that terminating process, and reclaim them back (without cooperation from the process code itself). With older operating systems it was not uncommon that this part was a bit flawed, and the OS kept running out of certain type of resources over time, requiring periodical reboots, but any solid OS (like most of the UNIX family ones) can run for years without intervention or leaking anything.
Why do we have garbage collectors and other means for memory management:
Because as a programmer of application you decide how many resources the app will use. If your app will run continuously in background and allocate new resources all the time, without releasing them, it will eventually exhaust the available resources of the OS, affecting the performance of whole machine.
But usually when you write application, you don't want to micro-manage memory, so if you allocate at one place 100 bytes, and at other place another 100 bytes, then you don't need them any more, but you need 200 bytes, you probably don't want to write complex code to reuse the discontinued 100+100 bytes from previous allocations, in most of the programming languages it is simpler to let their memory manager collect those earlier allocations (example: in C/C++ by free/delete
, unless you use your own memory allocator or garbage collector, in Java you simply drop all known references to the instances, and GC will figure out that memory is not needed by code and reclaim it), and allocate brand new 200 byte chunk.
So the memory managers and GC are convenience to programmer, to make it simpler to write common applications, which need to allocate only reasonable amount of memory and will release it back in timely manner.
Once you would work on some complex software, like for example computer game, then you need lot more skills, planning and care, because then performance matters and such naive approach to just allocate small chunks of memory as needed would end badly.
For example imagine particle system allocating/freeing memory for each particle, while the game emits thousands of them per minute, and they live just few seconds, that would lead to very fragmented memory manager state, which may lead to its collapse when the app will suddenly ask for large chunk of memory (or it will ask OS for another one, slowly growing memory usage over time, then the game will crash after few hours of playing because the free memory from OS exhausted). In such cases the programmer has to dig down into micro-managing its memory, for example allocating just once for the total lifetime of game process the big buffer for 10k particles, and keeping track itself which slots are used and which are freed, and handling situations gracefully when app request 10+k particles at the same time.
Another layer of hidden complexity (from programmer) is the OS capable to "swap" memory to disk. The app code is not aware that particular virtual memory address leads to non-existing physical memory, which is caught by OS, which knows that that piece of memory is actually stored on disk, so it does find some other free memory page (or swaps out some other page), reads the content from disk back, remaps that virtual address to the new physical memory address, and returns the control back to the process code, which tried to access those values (now available). If this sounds like terribly slow process, it is, that's why everything on the PC "crawls" when you exhaust free memory and the OS starts swapping memory to disk.
And that said, if you will ever program something which manipulates with sensitive values, *you* should clear unused memory after yourself, so it will not leak to some future process, which will receive from OS the same physical memory, after your process releases it (or terminates) (and in such case it's better to "clear" memory by trashing it with random values, as sometimes even amount of zeroes can leak some minor hint to the attacker, like how long was the encryption key, while random content is random = no info, as long as the RNG has enough entropy). Also it's good to know details about the particular language memory allocator, so you can in such applications use for example special allocator to guarantee the memory with sensitive data is not swappable to disk (as then the sensitive data end stored on disk in case of swapping), or for example in Java you don't use String
for sensitive data, because you know the Strings in Java have their own memory pool, and they are immutable, so if anyone manages to check content of your VM's string memory pool, it can read basically all String you ever used in your running app (I think there's some GC possible upon String pool too, if you exhaust it, but it's not being done by ordinary GC, ordinary GC reclaims just object instances, not String data). So in such cases you should as programmer to go great lengths to ensure you actually destroy the values themselves in the memory, when you don't need them any more, just releasing the memory is not enough.
when does the OS initilize the data for an example - when creating a variable or when the process starts to run?
Do you realize the code itself needs memory? The OS does load the executable from disk into memory, at first some fixed part of it where there are meta data stored, telling the OS how much of executable to load further (code + pre-initialized data), the size of the various sections (i.e. how much memory to allocate for data+bss sections), and suggested stack size. The binary data are loaded from the file into memory, so it effectively sets values of memory. Then the OS does prepare the runtime environment for the process, i.e. creates some virtual address space, sets up various sections, set access rights (like code part is read-only, and data is no-exec, if the OS is designed that way), meanwhile it keeps all this info in its internal structures, so it can later terminate process at will. Finally when the runtime environment is ready, it jumps to the entry point of code in user mode.
If it was some C/C++ application with default standard libraries, it will further adjust the environment to make the C runtime initialized, i.e. it will probably straight away allocate some basic heap memory from OS and set up the C memory allocator, prepare the stdin/stdout/stderr streams and connect to other OS services as needed, and finally call the main(...)
passing the argc/argv alongside. But things like global variables int x = 123;
were already part of binary, and loaded by OS, only the more dynamic things are initialized by libc upon start of app.
So the OS did allocate like for example 8MiB of RAM for code + data, and set up the virtual space. Since then it has no idea, what is the code of app doing (as long as it does not trigger some guardian, like accessing invalid memory, or doesn't call some OS service). OS doesn't have any idea whether the app did create some variable, or allocate some local variables on stack (at most it will notice when the stack grows outside of original allocated space by catching invalid memory access, at that point it may either crash the app, or it may remap more physical memory to the virtual address area where stack is [out]growing and make the app to continue with new stack memory available).
All the variables initializations/etc either happened while the binary was loaded (by OS), or they are fully under control of the app code.
If you call new
in C++, it will call the C runtime (that code is appended to your app when building executable), which will either provide you with memory from the already set up memory pool, or if it did run out of spare memory, it will call the OS heap allocation for some big chunk, which is then again managed by the C memory allocator from clib. Not every new/delete
does call OS, only very few of them, the micro management of that is done by the C runtime library, stored in the executable (or loaded dynamically from .DLL/.so files as needed through OS service to load dynamic code).
Just like JVM is actual application code, doing all sorts of housekeeping too, implementing also GC code, etc... And JVM must clear the allocated memory before passing it to Java's new
in the .class code, because that's how the Java language is defined. OS again has no idea what is going on, it doesn't even know that that app is virtual machine interpreting some java bytecodes from .class files, it's simply some process which did start and runs (and asks OS services as it wants).
You have to understand that the context switch of CPU mode between user mode and kernel mode is quite expensive operation. So if the app would call OS service every time some tiny amount of memory is modified, the performance would go down a lot. As modern computers have plenty of RAM, it's easier to provide the starting process with some 10-200MB area (based on meta data from executable) to start with, and let it handle situations when it needs more dynamically. But any reasonable app will minimize the OS services calls, that's why the clib has it's own memory manager and does not use the OS heap allocator for every new
(also the OS allocator may work with granularity which is unusable by common code, like for example allowing to allocate memory only in MiB chunks, etc.).
In high level languages like C/Java you have quite big part of app code provided by the standard libraries, so if you just started to learn that language and you didn't think how it works internally on machine code level, you may take all that functionality somehow for granted, or provided by OS. It's not, the OS provides only very basic and bare services, rest of the C/Java environment is provided by code which was linked into your application from standard libraries. If you create some "hello world" example in C, usually 90% of binary size is C runtime, and only few bytes are actually produced by you from that hello world source. And when you execute it, there are thousands of instructions executed (inside your process, from your binary, not counting the OS loader) before your main(...)
is even called.