I think one more downside that needs handling, is that it's better to cleanup other allocated data before exiting. I've seen a bunch of labels at the end of the function where the cleanup lives and then gotos to those labels depending on which malloc failed.
@@VivekYadav-ds8oz But it seems to be about C and explicit memory management here, not C++, because then you could teach std::unique_ptr, std::shared_ptr std::make_unique and std::make_shared and all other C++ features concerning OS resource handling.
What about using atexit() to register a function when xmalloc fails? Or better on_exit() and check for an specific exit code... (of course, assuming that xmalloc uses exit).
I think it's probably good to use a mixture of xmalloc and malloc, malloc where you need to handle the error and xmalloc where you don't. I don't think I'd really need something like xmalloc though because I don't write "malloc(...)" a lot, I try to allocate everything I need at the start of execution and if that's not possible I'll use a pool which only calls *alloc once or twice in a wrapper function. Heap fragmentation is not your friend.
Personally for memory allocation I created a library with a function that calls malloc and stores the pointer and the destructor function for that pointer in a static chained list, so when I want to exit failure I just call a function that calls destructors for each pointer and clears the list
This works in the general case, but it can cause double-free if you're not careful! Say some class manually de-allocates some member variables in its destructor (maybe it holds a _T* ptr_1_ instead of _std::unique_ptr ptr_1{}_ ). Now that's all fine and dandy, but since that class AND its member variables were both allocated via _custom_memory_allocator_ , it tries to call destructor of class and _ptr_1_ , leading to double-free of the object T behind _ptr_1_ . This is difficult because your entire codebase needs to be aware of the fact that there's a possiblity their memory allocator will clean their data for them, but only sometimes.
@@VivekYadav-ds8oz I'm using that memory allocator mostly for c programs, not c++ ones, and that's not the case because I keep `malloc` as the default allocator, say for example I want to allocate a struct with some allocated ptrs inside it, I have my constructor function that will just call `malloc` for the struct then for the pointers inside it, and my destructor function that will free ptrs inside then the struct, I pass both functions to my memory allocator then when an error occurs or the program ends I just call the destructor function that will call multiple frees, but you are right, it could be more optimized for c++, but I wrote it in c and use it for my c programs so this pronlem doesn't bother me
@@leokiller123able I imagine it must get hard to remember which were allocated by malloc() and which by custom_allocator. You mentioned "destructor functions" so I just assumed you were talking about C++ as there's no language support for destructors (I know you can make your own) in C.
Hey Jacob, I was watching one of your videos about memory usage and I had a very weird question, what happens if I increase the stack size using the ulimit -s command? Will I be able to run the same thing without allocating anything on the heap therefore making my program way faster?
Probably not, but it depends on the case. If the only reason you heap-allocate is because you're unsure of the size of the data structure before runtime (e.g. a vector), then just allocating the maximum size of the data structure on the stack (e.g. in case of a vector, say allocate an _int arr[5000];_ instead) will work just fine. But in some cases you NEED to do heap allocation, like if you want to share data between threads, you need to get that data off the stack unless you're very clear on the lifetimes of the concerned data and that it won't be destructed before all of the threads using it die. Example is std::shared_ptr. And this misses the fact that heap-allocation isn't as much of a bottleneck on performance as people think. The only place you'll feel the difference of not heap-allocating something is if it's in a hot loop (in which case heap allocation does prove to be very expensive).
the point of the stack is to automatically grow and shrink as you enter and exit function calls. this means that objects will get destroyed as soon as the function which created them exits. you cannot share an automatic object between two non-nested function calls. if you wanna share an object between any two function calls, you would have to create a static object (aka global object). the problem with that approach is that you have to know the exact amount of memory that will be needed during the execution (which is not possible in many use cases). because neither of these approaches fits the problem properly, dynamic memory was invented. dynamic memory is one smart way of solving the aforementioned issue. if not used, dynamic memory will not pollute the address space, but if needed it can always be acquired from the operating system (with mmap or some similar syscall) tl;dr : no, automatic memory (stack) cannot fit your use case every time (and it almost never will, actually)
Again, nonstandard, but I usually zmalloc keeps track of malloc and free calls to 1) tell you how much memory you've allocated and 2) help you find memory leaks.
Out of curiosity; at this point, do yall think comments saying "for statistics", "for the algorithm", etc yada yada yada even do anything at this point?
malloc returns nullptr for allocation failure is a historical mistake. Heap allocation should NEVER ever fail. just like stack overflow is undefined behavior.
Well, whether it *should* fail or not, in reality it *does* fail sometimes. So, if a process tries to malloc more memory than the OS can give it (say 20TB), what do you think it should do (instead of returning NULL)?
@@JacobSorber windows and linux would kill your process since you hit overcommit. which means in reality, heap allocation failure is the same as stack overflow because it is undefined behavior if malloc fails, it should be just a SILENT crash. just call __builtin_trap(). The mentality of allowing buggy process or corrupted process to run has caused enormous amount of security vulns, complexity and alls sorts of other pains. Even C++ would make the operator new fail fast in the future because there is no way you can handle it portablely and C++ EH has become a huge disaster since the HP back in the day uses EH to handle shit like allocation failure or programming bugs
@@coshvjicujmlqef6047 I think that's fair. Just the other day, I was playing with Linux, and ran into a case where malloc was returning NULL. There are certainly other cases where it does kill the process. Maybe in the latest kernel version this has been changed (I'm behind a few versions). I think I agree that termination is best for buggy cases (for all of the reasons you mention), but it's still a bit unsatisfying, since it assumes that heap exhaustion is always a bug and leaves a process few good cleanup options when it happens due to an unusually-large (but valid) input or a machine with fewer-than-usual resources (small embedded processor).
@@JacobSorber linux will kill your process randomly due to overcommit. Your argument is like hey we have a null derefence and process gets segmentation fault or gets segmantation fault by overflowing the stack but it should not fail lol. The problem is that the language abstraction machine has no way to deal with those errors. In fact, allocation failure is just undefined behavior. In general, without certain knowledge about that specific platform, you have no way to handle allocation failure.
I would have preferred if you had at least mentioned the memory leaks in your code... Ofcourse, in this small example it doesn't really matter, but if your program gets bigger and you keep on programming like this you're going to have a problem.
Yes, I'm sure I forget to mention something important in each of my videos (or many important things). But, I guess there's always the next video. :) If it's any consolation, I have talked about memory leaks in several of my past videos. And, I'm sure I'll talk about them again.
What’s the best lib for making https requests through C or C++?
I would check out libcurl.
Thank you for the video
I think one more downside that needs handling, is that it's better to cleanup other allocated data before exiting. I've seen a bunch of labels at the end of the function where the cleanup lives and then gotos to those labels depending on which malloc failed.
Well you can implement xmalloc by yourself, check if fails will cleanup all the allocated data before exit.
There's no point freeing memory before you exit, the os will take it back when the program exits anyway
@@pj20050 Resource manager objects (eg File, sockets, etc.) are an exception.
@@VivekYadav-ds8oz But it seems to be about C and explicit memory management here, not C++, because then you could teach std::unique_ptr, std::shared_ptr std::make_unique and std::make_shared and all other C++ features concerning OS resource handling.
there's also alloc, to allocate memory on the stack in stead of the heap.
"alloca"
I dont understand the point of doing this, as there are variable length arrays for that
@@leokiller123able Not all C compilers allow variable length arrays, MSVC for example.
@@Brock-Landers okay, so it's useful only in that specific case when you have to use another compiler than gcc or clang
@@leokiller123able Yet somehow it's still included in gcc 11.1.0. 'man alloca'
What about using atexit() to register a function when xmalloc fails? Or better on_exit() and check for an specific exit code... (of course, assuming that xmalloc uses exit).
I would use signals to handle errors for xmalloc and is there a way to access all of the stack memory?
Yes, the entire stack should be accessible.
@@JacobSorber I mean is there a way to print it or compare it?
I think it's probably good to use a mixture of xmalloc and malloc, malloc where you need to handle the error and xmalloc where you don't. I don't think I'd really need something like xmalloc though because I don't write "malloc(...)" a lot, I try to allocate everything I need at the start of execution and if that's not possible I'll use a pool which only calls *alloc once or twice in a wrapper function. Heap fragmentation is not your friend.
Personally for memory allocation I created a library with a function that calls malloc and stores the pointer and the destructor function for that pointer in a static chained list, so when I want to exit failure I just call a function that calls destructors for each pointer and clears the list
This works in the general case, but it can cause double-free if you're not careful!
Say some class manually de-allocates some member variables in its destructor (maybe it holds a _T* ptr_1_ instead of _std::unique_ptr ptr_1{}_ ). Now that's all fine and dandy, but since that class AND its member variables were both allocated via _custom_memory_allocator_ , it tries to call destructor of class and _ptr_1_ , leading to double-free of the object T behind _ptr_1_ .
This is difficult because your entire codebase needs to be aware of the fact that there's a possiblity their memory allocator will clean their data for them, but only sometimes.
@@VivekYadav-ds8oz I'm using that memory allocator mostly for c programs, not c++ ones, and that's not the case because I keep `malloc` as the default allocator, say for example I want to allocate a struct with some allocated ptrs inside it, I have my constructor function that will just call `malloc` for the struct then for the pointers inside it, and my destructor function that will free ptrs inside then the struct, I pass both functions to my memory allocator then when an error occurs or the program ends I just call the destructor function that will call multiple frees, but you are right, it could be more optimized for c++, but I wrote it in c and use it for my c programs so this pronlem doesn't bother me
@@leokiller123able I imagine it must get hard to remember which were allocated by malloc() and which by custom_allocator. You mentioned "destructor functions" so I just assumed you were talking about C++ as there's no language support for destructors (I know you can make your own) in C.
@@VivekYadav-ds8oz yeah by destructor I mean the function that will free the memory used by this pointer
that's pretty useless tbh, the OS already deallocates memory at the end of the program whether you want it or not.
Hi Jac sorber.. can you post a video on the increments in printfs.. it is really confusing!
Can you clarify what you mean by "increments in printfs"? I'm not understanding the issue.
@@JacobSorber I think he is talking about order of arguments evaluation. E.g if you write:
int i= 0;
printf("%d %d", ++i, i++)
Result can be tricky
Hey Jacob, I was watching one of your videos about memory usage and I had a very weird question, what happens if I increase the stack size using the ulimit -s command? Will I be able to run the same thing without allocating anything on the heap therefore making my program way faster?
Probably not, but it depends on the case.
If the only reason you heap-allocate is because you're unsure of the size of the data structure before runtime (e.g. a vector), then just allocating the maximum size of the data structure on the stack (e.g. in case of a vector, say allocate an _int arr[5000];_ instead) will work just fine.
But in some cases you NEED to do heap allocation, like if you want to share data between threads, you need to get that data off the stack unless you're very clear on the lifetimes of the concerned data and that it won't be destructed before all of the threads using it die. Example is std::shared_ptr.
And this misses the fact that heap-allocation isn't as much of a bottleneck on performance as people think. The only place you'll feel the difference of not heap-allocating something is if it's in a hot loop (in which case heap allocation does prove to be very expensive).
the point of the stack is to automatically grow and shrink as you enter and exit function calls. this means that objects will get destroyed as soon as the function which created them exits. you cannot share an automatic object between two non-nested function calls. if you wanna share an object between any two function calls, you would have to create a static object (aka global object). the problem with that approach is that you have to know the exact amount of memory that will be needed during the execution (which is not possible in many use cases). because neither of these approaches fits the problem properly, dynamic memory was invented.
dynamic memory is one smart way of solving the aforementioned issue. if not used, dynamic memory will not pollute the address space, but if needed it can always be acquired from the operating system (with mmap or some similar syscall)
tl;dr : no, automatic memory (stack) cannot fit your use case every time (and it almost never will, actually)
Nice Video. But My Question is When I Figure Out Redis Code, Then I Found Zmalloc Function. So Can You Explain What is Zmalloc and malloc or xmalloc.
Again, nonstandard, but I usually zmalloc keeps track of malloc and free calls to 1) tell you how much memory you've allocated and 2) help you find memory leaks.
What if instead of returning a null pointer of memory, you made the xmalloc give you memory mapped io to some storage?
for the algorithm
Thanks.
Out of curiosity; at this point, do yall think comments saying "for statistics", "for the algorithm", etc yada yada yada even do anything at this point?
malloc returns nullptr for allocation failure is a historical mistake. Heap allocation should NEVER ever fail. just like stack overflow is undefined behavior.
Well, whether it *should* fail or not, in reality it *does* fail sometimes. So, if a process tries to malloc more memory than the OS can give it (say 20TB), what do you think it should do (instead of returning NULL)?
@@JacobSorber windows and linux would kill your process since you hit overcommit. which means in reality, heap allocation failure is the same as stack overflow because it is undefined behavior
if malloc fails, it should be just a SILENT crash. just call __builtin_trap(). The mentality of allowing buggy process or corrupted process to run has caused enormous amount of security vulns, complexity and alls sorts of other pains.
Even C++ would make the operator new fail fast in the future because there is no way you can handle it portablely and C++ EH has become a huge disaster since the HP back in the day uses EH to handle shit like allocation failure or programming bugs
@@JacobSorber ruclips.net/video/ARYP83yNAWk/видео.html
@@coshvjicujmlqef6047 I think that's fair. Just the other day, I was playing with Linux, and ran into a case where malloc was returning NULL. There are certainly other cases where it does kill the process. Maybe in the latest kernel version this has been changed (I'm behind a few versions). I think I agree that termination is best for buggy cases (for all of the reasons you mention), but it's still a bit unsatisfying, since it assumes that heap exhaustion is always a bug and leaves a process few good cleanup options when it happens due to an unusually-large (but valid) input or a machine with fewer-than-usual resources (small embedded processor).
@@JacobSorber linux will kill your process randomly due to overcommit.
Your argument is like hey we have a null derefence and process gets segmentation fault or gets segmantation fault by overflowing the stack but it should not fail lol. The problem is that the language abstraction machine has no way to deal with those errors. In fact, allocation failure is just undefined behavior.
In general, without certain knowledge about that specific platform, you have no way to handle allocation failure.
I would have preferred if you had at least mentioned the memory leaks in your code... Ofcourse, in this small example it doesn't really matter, but if your program gets bigger and you keep on programming like this you're going to have a problem.
Yes, I'm sure I forget to mention something important in each of my videos (or many important things). But, I guess there's always the next video. :)
If it's any consolation, I have talked about memory leaks in several of my past videos. And, I'm sure I'll talk about them again.
Comment for the algorithm.
hi
python programmer disliked)