What happens when blocks are freed from heap by free()? - c

So I have allocated 256 blocks in heap:
char* ptr1 = malloc(128);
char* ptr2 = malloc(128);
Now after I free ptr2 which I assume currently lies on top of the heap, the program break(the current location of the heap) does not decrease. However if I do another malloc the address returned by malloc is the same as the one that is freed.
So I have the following questions:
When I free a block why does not the program break decrease?
When I call free what exactly happens?How does it keep track of the freed memory so that next time I declare malloc the address is the same?

It's unspecified behavior. You can not rely on any single answer, unless you only care about one particular platform/os/compiler/libc combination. You did not specify an OS, and the C standard does not describe, or require any particular implementation. From C99 (I don't have the final published version of C11 yet):
7.20.3
The order and contiguity of storage allocated by successive calls to
the calloc, malloc, and realloc functions is unspecified. The pointer
returned if the allocation succeeds is suitably aligned so that it may
be assigned to a pointer to any type of object and then used to access
such an object or an array of such objects in the space allocated
(until the space is explicitly deallocated). The lifetime of an
allocated object extends from the allocation until the deallocation.
Each such allocation shall yield a pointer to an object disjoint from
any other object. The pointer returned points to the start (lowest
byte address) of the allocated space. If the space cannot be
allocated, a null pointer is returned. If the size of the space
requested is zero, the behavior is implementation- defined: either a
null pointer is returned, or the behavior is as if the size were some
nonzero value, except that the returned pointer shall not be used to
access an object.

This manual of GNU libc , might be of help.
Here's the gist
Occasionally, free can actually return memory to the operating system
and make the process smaller. Usually, all it can do is allow a later
call to malloc to reuse the space. In the meantime, the space remains
in your program as part of a free-list used internally by malloc.

When I free a block why does not the program break decrease?
I believe it doesn't decrease because that memory has already been given to the program.
When I call free() what exactly happens?
That section of memory is marked as allocatable, and its previous contents can be overwritten.
Consider this example...
[allocatedStatus][sideOfAllocation][allocatedMemory]
^-- Returned pointer
Considering this, the free() can then mark the [allocatedStatus] to false, so future allocations on the heap can use that memory.
How does it keep track of the free()d memory so that next time I declare
malloc() the address is the same?
I don't think it does. It just scanned for some free memory and found that previous block that had been marked as free.

Here is a rough idea how memory allocators work:
You have an allocator that has a bunch of "bins" ("free lists") which are just linked lists of free memory blocks. Each bin has a different block size associated with it (I.e.: you can have a list for 8 byte blocks, 16 byte blocks, 32 byte blocks, etc... Even arbitrary sizes like 7 or 10 byte blocks). When your program requests memory (usually through malloc()) the allocator goes to the smallest bin that would fit your data and checks to see if there are any free memory blocks in it. If not then it will request some memory from the OS (usually called a page) and cuts the block it gets back into a bunch of smaller blocks to fill the bin with. Then it returns one of these free blocks to your program.
When you call free, the allocator takes that memory address and puts it back into the bin (aka free list) it came from and everybody is happy. :)
The memory is still there to use so you don't have to keep paging memory, but with respect to your program it is free.

I believe it's entirely up to the operating system once you call free(), it may choose to immediately reclaim that memory or not care and just mark that memory segment as a possible acquisition for a later time (likely the same thing). To my knowledge that memory (if significant) shows up as available in the task manager right after free() on windows.
Keep in mind that the memory we are talking about here is virtual. So that means the operating system can tell you anything it wants and is likely not an accurate representation of the physical state of the machine.
Think about how you would manage memory allocation if you were writing an OS, you likely wouldn't want to do anything hasty that may waste resources. We are talking about 128 bytes here, would you want to waste valuable processing time handling it alone? It may be the reason for that behavior or not, at least plausible.
Do it in a loop and then free() in another loop or just allocate big chunks of memory, see what happens, experiment.

Related

Is the memory allocation done by malloc always contiguous?

I have read in the K&R book that "the space that malloc manages may not be contiguous. each block contains size ,a pointer to next block.". But when i did google i saw that most of the people are saying that malloc always does contiguous memory allocation. So please clear this doubt .What is the fact? The space that malloc manages is always contiguous or may or may not be contiguous.
A single block of memory returned by malloc, calloc or realloc will always be contiguous.
The next block of memory returned by a separate alloc call can be at any address and does not need to be contiguous with the first block.
Look at the diagram on the very page you read that sentence:
The region with dots is not owned by malloc, and is right between two regions that malloc manages. The region not owned by malloc is mostly likely allocated by another part of the program (for example, using mmap(2)). It could also just be not allocated.
Despite this 'hole' in the region of memory managed by malloc, malloc (and it's family of functions) only allocate blocks of memory that are contiguous.
So, the region managed by malloc could be discontiguous, but the blocks allocated by malloc are contiguous.
The memory segment you receive from a single call to malloc() is always contiguous. There's no warranty on the segments returned from different calls. It's possible that you get that impression at program start on the first calls to malloc, but as memory is returned to the heap with free() the memory returned is reused and finally you end with more or less amount of dispersion, making different calls to return completely unrelated pointers.
On other side, malloc() uses sbrk(2) system call that increases/decreases the size of the data segment, if possible. But in the case it cannot use sbrk(), malloc() uses memmap() to get one or more heaps to operate. Memmap() was not available in unix systems when that book was written, so in principle things can have changed a lot since then. Those heaps (mmap()ed) are mapped into the virtual space of the process, based on many possible situations by the kernel, not by malloc(), like the loading of dynamic shared objects, and this clashes with the enforcement of continuity in the address space. Probably the addresses will show ranges of discontinous memory interspersed with other ranges of completely different things, like physical memory attachments (like video memory if the operating system offers this service) memory mapped files, shared libraries, etc.
On other side, malloc must allocate some data for each chunk of memory that it gives to you.... Many implementations locate this chunk on one side of the given segment, so even if the chuncks were contiguous, you cannot normally use the memory between them or you'll corrupt the memory malloc() uses to handle the heap.

Why use calloc to initialize the allocated memory to zero?

We know that the heap is an area of demand-zero memory that begins immediately after the uninitialized data area and grows upward (toward higher addresses). By demand-zero, it means the first time the CPU touches a virtual page in heap area, the corresponding physical page will be all zeros.
If that is the case, then why there is a function calloc used to initialize the allocated memory to zero? Why do demand-zero pages need to be initialized zero again if they will be zero already when accessed?
Because after you've used the space and released it with free(), it might be allocated again. If you don't use calloc(), there's no guarantee that the memory will be zeroed on the second time it is used. (Calling free() does not zero the space.)
calloc does not necessarily have to initialize the memory to zero by itself. The description of calloc says that:
The space is initialized to all bits zero.
but it does not say that it is calloc that does this, just that the memory is initialized to zero by some mechanism. This is unlike malloc:
The malloc function allocates space for an object whose size is specified by size and whose value is indeterminate.
calloc guarantees that the memory is zeroed, and malloc does not. If the contents of the block are copy-on-write zero pages, then calloc may know not to zero it again and is faster than malloc + memset, as memset would not know that the memory was already zeroed (unless the compiler optimizes malloc + memset(..., 0, ...) to calloc); on the other hand if the block is reused, then calloc needs to zero it, even if the caller would not care about zeroing, therefore a malloc would be faster than calloc if no zeroing is needed, because then calloc would indeed do effectively malloc + memset
In short, it's more portable to set the memory to zero explicitly if that's what your application needs; and faster to leave it alone if it doesn't. You don't have to use calloc() to get zero'd memory -- you could just use malloc() and zero it yourself (e.g.,using memset). But calloc() will give you zero'd memory more quickly, if it can take advantage of a platform-specific feature to get it.
the 'heap' memory will contain what ever trash was in memory when your program was loaded, (just like the stack)
It is up to your application to zero memory. There are a few ways to do that.
calloc()
a loop in your code
memset()
This is in part historical, and in part as a useful feature
We had heap with uninitialised memory (which was not zero), so more quick to get memory. Then this was discovered that this is a security problem, so much later, you get memory cleared to zero (and never memory set by other processes). Do not assume all systems will do it (especially on small embedded CPU, where CPU and bus access are expensive (time and power).
calloc is very handy, when you allocate arrays (as you see, the signature is done for arrays). Often on arrays, you want to initialize values to zero. A loop is very slow, and static had already the initialization to zero. We have two possibilities: initialized memory with calloc or uninitialised memory with malloc.
Note: malloc doesn't guarantee to give you all zeros: it could give you already allocated (from your process) and freed memory. Just now new memory given by kernel is zeroed (e.g. with brk/sbrk, which is sometime called by malloc/calloc, in case of lack of free memory in existing heap memory). These are two different allocation of memory.

malloc adjacent block of memory?

I'm trying to figure out how many bytes in a block are taken up by the boundary tags. I have been told that when trying to malloc an adjacent block of memory, a "jump" will appear in assembly code, and I can use that to determine the size of the boundary tag. I've tried this:
int* arr = malloc(8);
arr++;
arr = malloc(8);
But there isn't any jump in assembly code. Am I "trying to malloc an adjacent block of memory"?
EDIT: I think he means a jump will appear between address value. I use the beginning of the second block of memory subtract the payload size of the first block. But I'm still confused, how could I malloc an adjacent block of memory?
Unless you're writing an actual memory allocator, you can't actually allocate two consecutive chunks of memory. If you want to see some pretty gnarly code which does this, have a look at the Illumos malloc https://github.com/illumos/illumos-gate/blob/master/usr/src/lib/libc/port/gen/malloc.c.
If you want to see how Illumos (and Solaris) handle the redzone between allocated blocks, you should trawl through https://github.com/illumos/illumos-gate/tree/master/usr/src/lib/libumem/common.
The memory consumed by malloc(3) requires, for proper management of the actually used memory, of some structures that must be dynamically allocated also. For this reason, many allocators just do allocate the space required for the management data adjacent to the block space dedicated to the user. This makes that normally two consecutive junks of memory allocated by malloc(2) show some gap in their addresses.
There are other reasons to see gaps, one fundamental is that malloc normally gives you aligned memory addresses, so it is warranted that the data you store on that memory will be properly aligned.
And of course, there can be implementations (normally when heap allocation should be more robust in respect to buffer overruns) that the memory dedicated to storage of management data is completely unrelated and apart off the final given memory. In this case you could observe no gaps between memory allocations on some cases.
Anyway, your code has serious bugs, let's see:
int* arr = malloc(8);
You had better here to acquire just the memory you need, using the sizeof operator, as in int *arr = malloc(sizeof *arr); instead.
arr++;
this statement is useless, as you are going to overwrite the value of arr (the pointer) with new assignment statement after it from malloc(), so it is of no use to increment the pointer value. You also are somewhat losing the returned value of the previous malloc() (which is essential in order to return the allocated memory, but read below).
arr = malloc(8);
Until here, you had the chance to --arr decrementing the value of arr in order to be capable of free(3) that block. But this statement overwrites the value stored in arr so the previous pointer value is overwritten by the new pointer. Memory you acquired on the first malloc has no way to be accessed again. This is what is commonly known as a memory leak, and is normally a serious error (very difficult to catch) on long run programs (like servers or system daemons). The program allocates a bunch of memory in the inner part of a loop, that is not returned back with a call to free(3), so the program begins growing and growing until it exhausts all the available memory.
A final note, I don't understand what did you mean with malloc adjacent block of memory. Did you believe that incrementing the pointer would make malloc() to give you a special block of memory?
First, malloc has no idea of what are you going to do with the pointer it gives to you.
But also, it doesn't know anything about the variable contents of the pointer you are assigning to (you can even not store it in a variable, and pass it as another parameter to another functions) So there's no possibility for malloc to know that you have incremented the pointer value, or even the pointer location, from its body.
I cannot guess your interpretation. It would be nice to know what has made you to think that you can control how malloc(3) selects the block of memory to give to you. You have no control on the internals of malloc() You just specify the amount of continous memory you want, and mallocs provides it, giving you a pointer pointing to the start of that block. You cannot assume that the next time you call malloc (with the same or different amount of memory) it will give you an adjacent block. It just can be completely unrelated (above or below in memory) to the previous given block. And you cannot modify that pointer, because you need it to call free(3) once you don't need the block anymore, with exactly the same pointer value that malloc(3) gave to you. If, for some reason you modify the pointer, you need to restore it to the original value to be capable of calling free(3). Lack to do so, you'll probably crash your program at the next call to free(3).
I just see a memory leak. Malloc 2 times into different vars 8 bytes of space and see if the difference is more than 8 bytes or 2 int.

Calling free on a pointer twice

I have been taught in lectures, that calling free() on a pointer twice is really, really bad. I know that it is good practice, to set a pointer to NULL, right after having freed it.
However, I still have never heard any explanation as to why that is. From what I understand, the way malloc() works, it should technically keep track of the pointers it has allocated and given you to use. So why does it not know, whether a pointer it receives through free() has been freed yet or not?
I would love to understand, what happens internally, when you call free() on a location that has previously already been freed.
When you use malloc you are telling the PC that you want to reserve some memory location on the heap just for you. The computer gives back a pointer to the first byte of the addressed space.
When you use free you are actually telling the computer that you don't need that space anymore, so it marks that space as available for other data.
The pointer still points to that memory address. At this point that same space in the heap can be returned by another malloc call. When you invoke free a second time, you are not freeing the previous data, but the new data, and this may not be good for your program ;)
To answer your first question,
So why does it not know, whether a pointer it receives through free() has been freed yet or not?
because, the specification for malloc() in C standard does not mandate this. When you call malloc() or family of functions, what it does is to return you a pointer and internally it stores the size of the memory location allocated in that pointer. That is the reason free() does not need a size to clean up the memory.
Also, once free()-d, what happens with the actually allocated memory is still implelentation dependent. Calling free() is just a marker to point out that the allocated memory is no longer in use by the process and can be reclaimed and e re-allocated, if needed. So, keeping track of the allocated pointer is very needless at that point. It will be an unnecessary burden on the OS to keep all the backtracks.
For debugging purpose, however, some library implementations can do this job for you, like DUMA or dmalloc and last but not the least, memcheck tool from Valgrind.
Now, technically, the C standard does not specify any behaviour if you call free() on an already free-ed pointer. It is undefined behavior.
C11, chapter ยง7.22.3.3, free() function
[...] if
the argument does not match a pointer earlier returned by a memory management
function, or if the space has been deallocated by a call to free() or realloc(), the
behavior is undefined.
C standard only says that calling free twice on a pointer returned by malloc and its family function invoke undefined behavior. There is no further explanation why it is so.
But, why it is bad is explained here:
Freeing The Same Chunk Twice
To understand what this kind of error might cause, we should remember how the memory manager normally works. Often, it stores the size of the allocated chunk right before the chunk itself in memory. If we freed the memory, this memory chunk might have been allocated again by another malloc() request, and thus this double-free will actually free the wrong memory chunk - causing us to have a dangling pointer somewhere else in our application. Such bugs tend to show themselves much later than the place in the code where they occured. Sometimes we don't see them at all, but they still lurk around, waiting for an opportunity to rear their ugly heads.
Another problem that might occure, is that this double-free will be done after the freed chunk was merged together with neighbouring free chunks to form a larger free chunk, and then the larger chunk was re-allocated. In such a case, when we try to free() our chunk for the 2nd time, we'll actually free only part of the memory chunk that the application is currently using. This will cause even more unexpected problems.
When you are calling malloc you are getting a pointer. The runtime library needs to keep track of the malloced memory. Typically malloc does not store the memory management structures separated from the malloc ed memory but in one place. So a malloc for x bytes in fact takes x+n bytes, where one possible layout is that the first n bytes are containing a linked list struct with pointers to the next (and maybe previous) allocated memory block.
When you free a pointer then the function free could walk through it's internal memory management structures and check if the pointer you pass in is a valid pointer that was malloced. Only then it could access the hidden parts of the memory block. But doing this check would be very time consuming, especially if you allocate a lot. So free simply assumes that you pass in a valid pointer. That means it directly access the hidden parts of the memory block and assumes that the linked list pointers there are valid.
If you free a block twice then you might have the problem that someone did a new malloc, got the memory you just freed, overwrites it and the second free reads invalid pointers from it.
Setting a freed pointer to NULL is good practice because it helps debugging. If you access freed memory your program might crash, but it might also just read suspicious values and maybe crash later. Finding the root cause then might be hard. If you set freed pointers to NULL your program will immediately crash when you try to access the memory. That helps massively during debugging.

Understanding C Memory Allocation and Deallocation

I have been recently trying to learn how to program in the C programming language.
I am currently having trouble understanding how memory is deallocated by free() in C.
What does it mean to free or release the memory?
For instance, if I have the following pointer:
int *p = malloc(sizeof(int));
When I deallocate it using free(p), what does it do? Does it somehow flag it as "deallocated", so the application may use it for new allocations?
Does it deallocates only the pointer address, or the address being pointed is also deallocated too?
I would do some experiments myself to better understand this, but I am so newbie in the subject that I don't know even how to debug a C program yet (I'm not using any IDE).
Also, what if int *p is actually a pointer to an array of int?
If I call free(p), does it deallocate the whole array or only the element it is pointing to?
I'm so eager to finally understand this, I would very much appreciate any help!
What does it mean to free or release the memory?
It means that you're done with the memory and are ready to give it back to the memory allocator.
When I deallocate it using free(p), what does it do?
The specifics are implementation dependent, but for a typical allocator it puts the block back on the free list. The allocator maintains a list of blocks that are available for use. When you ask for a chunk of memory (by calling malloc() or similar) the allocator finds an appropriate block in the list of free blocks, removes it (so it's no longer available), and gives you a pointer to the block. When you call free(), the process is reversed -- the block is put back on the free list and thereby becomes available to be allocated again.
Importantly, once you call free() on a pointer, you must not dereference that pointer again. A common source of memory-related errors is using a pointer after it has been freed. For that reason, some consider it a helpful practice to set a pointer to nil immediately after freeing it. Similarly, you should avoid calling free() on a pointer that you didn't originally get from the allocator (e.g. don't free a pointer to a local variable), and it's never a good idea to call free() twice on the same pointer.
Does it deallocates only the pointer address, or the address being pointed is also deallocated too?
When you request a block of memory from the allocator, you specify the size of the block you want. The allocator keeps track of the size of the block so that when you free the block, it knows both the starting address and the block size. When you call free(p), the block that p points to is deallocated; nothing happens to the pointer p itself.
Also, what if int *p is actually a pointer to an array of int?
An array in C is a contiguous block of memory, so a pointer to the first element of the array is also a pointer to the entire block. Freeing that block will properly deallocate the entire array.
I'm so eager to finally understand this, I would very much appreciate any help!
There are a number of good pages about memory allocation in C that you should read for a much more detailed understanding. One place you could start is with the GNU C Library manual section on memory allocation.
As alluded to above and in the other answers, the actual behavior of the allocator depends on the implementation. Your code shouldn't have any particular expectations about how memory allocation works beyond what's documented in the standard library, i.e. call malloc(), calloc(), etc. to get a block of memory, and call free() to give it back when you're done so that it can be reused.
malloc and free do whatever they want. Their expected behaviour is that malloc allocates a block of desired size in dynamic memory and returns a pointer to it. free must be able to receive one such pointer and correctly deallocate the block. How they keep track of the block size is irrelevant.
Is int *p a pointer to an array of ints ? Maybe. If you allocated sufficient space for several ints, yes.
There is a fixed and limited amount of memory in your computer, and everybody wants some. The Operating system is charged with the task of assigning ownership to pieces of memory and keeping track of it all to assure that no one messes with anyone else's.
When you ask for memory with malloc(), you're asking the system (the C runtime and the OS) to give you the address of a block of memory that is now yours. You are free to write to it and read from it at will, and the system promises that no one else will mess with it while you own it. When you de-allocate it with free(), nothing happens to the memory itself, it's just no longer yours. What happens to it is none of your business. The system may keep it around for future allocations, it may give it to some other process.
The details of how this happens vary from one system to another, but they really don't concern the programmer (unless you're the one writing the code for malloc/free). Just use the memory while it's yours, and keep your hands off while it's not.

Resources