Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do 'malloc' and 'new' work? How are they different (implementation wise)? [duplicate]

Tags:

c++

c

I know how they are different syntactically, and that C++ uses new, and C uses malloc. But how do they work, in a high-level explanation?

See What is the difference between new/delete and malloc/free?

like image 508
Joel Avatar asked Dec 13 '08 22:12

Joel


3 Answers

I'm just going to direct you to this answer: What is the difference between new/delete and malloc/free? . Martin provided an excellent overview. Quick overview on how they work (without diving into how you could overload them as member functions):

new-expression and allocation

  1. The code contains a new-expression supplying the type-id.
  2. The compiler will look into whether the type overloads the operator new with an allocation function.
  3. If it finds an overload of an operator new allocation function, that one is called using the arguments given to new and sizeof(TypeId) as its first argument:

Sample:

new (a, b, c) TypeId;

// the function called by the compiler has to have the following signature:
operator new(std::size_t size, TypeOfA a, TypeOfB b, TypeOf C c);
  1. if operator new fails to allocate storage, it can call new_handler, and hope it makes place. If there still is not enough place, new has to throw std::bad_alloc or derived from it. An allocator that has throw() (no-throw guarantee), it shall return a null-pointer in that case.
  2. The C++ runtime environment will create an object of the type given by the type-id in the memory returned by the allocation function.

There are a few special allocation functions given special names:

  • no-throw new. That takes a nothrow_t as second argument. A new-expression of the form like the following will call an allocation function taking only std::size_t and nothrow_t:

Example:

new (std::nothrow) TypeId;
  • placement new. That takes a void* pointer as first argument, and instead of returning a newly allocated memory address, it returns that argument. It is used to create an object at a given address. Standard containers use that to preallocate space, but only create objects when needed, later.

Code:

// the following function is defined implicitly in the standard library
void * operator(std::size_t size, void * ptr) throw() {
    return ptr;
}

If the allocation function returns storage, and the the constructor of the object created by the runtime throws, then the operator delete is called automatically. In case a form of new was used that takes additional parameters, like

new (a, b, c) TypeId;

Then the operator delete that takes those parameters is called. That operator delete version is only called if the deletion is done because the constructor of the object did throw. If you call delete yourself, then the compiler will use the normal operator delete function taking only a void* pointer:

int * a = new int;
=> void * operator new(std::size_t size) throw(std::bad_alloc);
delete a;
=> void operator delete(void * ptr) throw();

TypeWhosCtorThrows * a = new ("argument") TypeWhosCtorThrows;
=> void * operator new(std::size_t size, char const* arg1) throw(std::bad_alloc);
=> void operator delete(void * ptr, char const* arg1) throw();

TypeWhosCtorDoesntThrow * a = new ("argument") TypeWhosCtorDoesntThrow;
=> void * operator new(std::size_t size, char const* arg1) throw(std::bad_alloc);
delete a;
=> void operator delete(void * ptr) throw();

new-expression and arrays

If you do

new (possible_arguments) TypeId[N];

The compiler is using the operator new[] functions instead of plain operator new. The operator can be passed a first argument not exactly sizeof(TypeId)*N: The compiler could add some space to store the number of objects created (necassary to be able to call destructors). The Standard puts it this way:

  • new T[5] results in a call of operator new[](sizeof(T)*5+x), and
  • new(2,f) T[5] results in a call of operator new[](sizeof(T)*5+y,2,f).
like image 63
Johannes Schaub - litb Avatar answered Oct 08 '22 17:10

Johannes Schaub - litb


What new does differently form malloc is the following:

  • It constructs a value in the allocated memory, by calling operator new. This behaviour can be adapted by overloading this operator, either for all types, or just for your class.
  • It calls handler functions if no memory can be allocated. This gives you the opportunity to free the required memory on the fly if you have registered such a handler function beforehand.
  • If that doesn't help (e.g. because you didn't register any function), it throws an exception.

So all in all, new is highly customizable and also does initialization work besides memory allocation. These are the two big differences.

like image 28
Konrad Rudolph Avatar answered Oct 08 '22 17:10

Konrad Rudolph


Although malloc/free and new/delete have different behaviors, they both do the same thing at a low level: manage dynamically allocated memory. I'm assuming this is what you're really asking about. On my system, new actually calls malloc internally to perform its allocation, so I'll just talk about malloc.

The actual implementation of malloc and free can vary a lot, since there are many ways to implement memory allocation. Some approaches get better performance, some waste less memory, others are better for debugging. Garbage collected languages may also have completely different ways of doing allocation, but your question was about C/C++.

In general, blocks are allocated from the heap, a large area of memory in your program's address space. The library manages the heap for you, usually using system calls like sbrk or mmap. One approach to allocating blocks from the heap is to maintain a list of free and allocated blocks which stores block sizes and locations. Initially, the list might contain one big block for the whole heap. When a new block is requested, the allocator will select a free block from the list. If the block is too large, it can be split into two blocks (one of the requested size, the other of whatever size is left). When an allocated block is freed, it can be merged with adjacent free blocks, since having one big free block is more useful than several small free blocks. The actual list of blocks can be stored as separate data structures or embedded into the heap.

There are many variations. You might want to keep separate lists of free and allocated blocks. You might get better performance if you have separate areas of the heap for blocks of common sizes or separate lists for those sizes. For instance, when you allocated a 16-byte block, the allocator might have a special list of 16-byte blocks so allocation can be O(1). It may also be advantageous to only deal with block sizes that are powers of 2 (anything else gets rounded up). For instance, the Buddy allocator works this way.

like image 36
Jay Conrod Avatar answered Oct 08 '22 17:10

Jay Conrod