Plugging the memory leak, C/C++ Programming

Assignment Help:

Problem #1: plugging the memory leak. When you employ the "normal" new operator, for example Foo* p = new Foo(), the compiler generates some special code to manage the case while the constructor throws an exception. The actual code produced by the compiler is functionally similar to this:

// It is functionally what happens with Foo* p = new Foo() Foo* p;

// don't catch exceptions thrown through the allocator itself void* raw = operator new(sizeof(Foo));

// catch any exceptions thrown through the ctor try {

p = new(raw) Foo(); // call the ctor with raw as this

}

catch (...) {

// oops, ctor threw an exception operator delete(raw);

throw; // rethrow the ctor's exception

}

The point is which the compiler deallocates the memory if the ctor throws an exception. However in particular case of the "new with parameter" syntax (called commonly "placement new"), the compiler won't know that what to do if the exception take places so by default it does nothing:

// It is functionally what happens with Foo* p = new(pool) Foo():

void* raw = operator new(sizeof(Foo), pool);

// the above function returns "pool.alloc(sizeof(Foo))" simply

Foo* p = new(raw) Foo();

// if the above line "throws", pool.dealloc(raw) is NOT called

Thus the goal is to force the compiler to do something same to what it does with the global new operator. Luckily it's simple: while the compiler sees new(pool) Foo(), this looks for a corresponding operator delete. If it determines one, it does the equivalent of wrapping the ctor call in a try block as illustrated above. Thus we would provide simply an operator delete with the following signature (be careful to obtain this right; if the second parameter has a distinct type from the second parameter of the operator new(size_t, Pool&), the compiler doesn't protest; it bypasses the try block simply when your users say new(pool) Foo()):

void operator delete(void* p, Pool& pool)

{

pool.dealloc(p);

}

After this, the compiler will wrap the ctor calls of your new expressions automatically in a try block:

// This is functionally what happens along with Foo* p = new(pool) Foo() Foo* p;

// don't catch exceptions thrown through the allocator itself void* raw = operator new(sizeof(Foo), pool);

// the above returns "pool.alloc(sizeof(Foo))" simply

// catch any exceptions thrown through the ctor try {

p = new(raw) Foo(); // call the ctor along with raw as this

}

catch (...) {

// oops, ctor threw an exception

operator delete(raw, pool); // that's the magical line!!

throw; // rethrow the ctor's exception

}

In other words, the one-liner function operator delete(void* p, Pool& pool) causes the compiler to automagically plug the memory leak. Certainly that function can be, but doesn't need to be, inline.

Problems #2 ("ugly thus error prone") and #3 ("users have to manually associate pool-pointers along with the object that allocated them, that is error prone") are simultaneously solved along an additional 10-20 lines of code in one place. In other terms, we add 10-20 lines of code in one place (your Pool header file) and make simpler an arbitrarily large number of other places (each piece of code which uses your Pool class).

The idea is to implicitly linked a Pool* with every allocation. The Pool* linked with the global allocator would be NULL, however at least conceptually you could say each allocation has linked Pool*. Then you replace the global operator delete thus it looks up the linked Pool*, and if non-NULL, calls that Pool's deallocate function. For instance, if(!) the normal deallocator utilized free(), the replacment for the global operator delete would look something as:

void operator delete(void* p)

{

if (p != NULL) {

Pool* pool = /* somehow get the associated 'Pool*' */;

if (pool == null)

free(p);

else

pool->dealloc(p);

}

}

If you're not definite if the normal deallocator was free(), the simplest approach is also replace the global operator new with something which uses malloc(). The replacement in support of the global operator new would look like this (note: this definition avoid a few details like the new_handler loop and the throw std::bad_alloc() which happens if we run out of memory):

void* operator new(size_t nbytes)

{

if (nbytes == 0)

nbytes = 1; // thus all alloc's get a distinct address void* raw = malloc(nbytes);

...somehow associate the NULL 'Pool*' with 'raw'... return raw;

}

The only remaining difficulty is to linked a Pool* with an allocation. One approach, utilized in at least one commercial product, is to employ a std::map. In other terms, build a look-up table whose keys are allocation-pointer and whose values are the linked Pool*. It is necessary that you insert a key/value pair into the map only in operator new(size_t,Pool&). Particularly, you ought to not insert a key/value pair from the global operator new (for example you have to not say, poolMap[p] = NULL in the global operator new).

Reason: doing that would develop a nasty chicken-and-egg difficulty since std::map probably employ the global operator new, it ends up inserting a new entry each time inserts a new entry, leading to infinite recursion bang you're dead.

Although this technique needs a std::map look-up for each deallocation, it appears to have acceptable performance, at least in several cases.

Another approach which is faster however might use more memory and is a little trickier is to prepend a Pool* just before all of allocations. For instance, if n bytes was twenty four, meaning the caller was asking to allocate 24 bytes, we would allocate 28 (or 32 if the machine need 8-byte alignment for things as doubles and/or long longs), stuff the Pool* to the first four bytes, and return the pointer 4 (or 8) bytes from the starting of what you allocated. After that your global operator delete backs off the 4 (or 8) bytes, determine the Pool*, and if NULL, uses free() or else calls pool- >dealloc(). The parameter passed to free () and pool->dealloc() would be the pointer 4 (or 8) bytes to left of the original parameter, p. If (!) you decide on four byte alignment, your code would look like this (although as before, the following operator new code elides common out-of-memory handlers):

void* operator new(size_t nbytes)

{

if (nbytes == 0)

nbytes = 1; // so all alloc's get a distinct address

void* ans = malloc(nbytes + 4); // overallocate by 4 bytes

*(Pool**)ans = NULL; // use NULL in the global new return (char*)ans + 4; // don't let users see the Pool*

}

void* operator new(size_t nbytes, Pool& pool)

{

if (nbytes == 0)

nbytes = 1; // thus all alloc's get a distinct address

void* ans = pool.alloc(nbytes + 4); // overallocate through 4 bytes

*(Pool**)ans = &pool; // put the Pool* here

return (char*)ans + 4; // don't allow users see the Pool*

}

void operator delete(void* p)

{

if (p != NULL) {

p = (char*)p - 4; // back off to the Pool* Pool* pool = *(Pool**)p;

if (pool == null)

free(p); // note: 4 bytes left of the original p else

pool->dealloc(p); // note: four bytes left of the original p

}

}

Of course the last few paragraphs of this FAQ are viable only while you are allowed to modify the global operator new and operator delete. If you are not allowed to alter these global functions, the primary three quarters of this FAQ is still applicable.


Related Discussions:- Plugging the memory leak

Write a recursive function with cylinders, I need to make a fractal trees i...

I need to make a fractal trees in OpenGL C++ . The method used is to write a recursive function with cylinders of different sizes and angles to create the tree trunk and branches.

Sequence for DMA controller, Write a sequence of instructions that transfer...

Write a sequence of instructions that transfers data from memory to an external I/O device by using channel 3 of the 8237 DMA controller. Transfer from 20000H-20FFFH.

How many levels deep can include files be nested, How many levels deep can ...

How many levels deep can include files be nested? - As such, there is no limit to number of levels of nested include files you can have however your compiler might run out of s

C++ program, Receive 3 numbers and display them in ascending order from sma...

Receive 3 numbers and display them in ascending order from smallest to largest ed#

ARRAY, A PROGRAM TO CALCULATE AREA OF TRIANGLE

A PROGRAM TO CALCULATE AREA OF TRIANGLE

Decode the code, Smugglers are becoming very smart day by day. Now they hav...

Smugglers are becoming very smart day by day. Now they have developed a new technique of sending their messages from one smuggler to another. In their new technology, they are send

Luminous Jewels - The Polishing Game, Byteland county is very famous for lu...

Byteland county is very famous for luminous jewels. Luminous jewels are used in making beautiful necklaces. A necklace consists of various luminous jewels of particular colour. Nec

Program, Byteland county is very famous for luminous jewels. Luminous jewel...

Byteland county is very famous for luminous jewels. Luminous jewels are used in making beautiful necklaces. A necklace consists of various luminous jewels of particular colour. Nec

multithreaded server, Implement a multithreaded server that can be used as...

Implement a multithreaded server that can be used as a proxy server to access some designated file (say it reflects the proxy copy of two file1.txt, file2.txt). Now use a client to

Implement binary heap in c++?, A:BinaryHeap.h ------------ #ifndef BI...

A:BinaryHeap.h ------------ #ifndef BINARY_HEAP_H_ #define BINARY_HEAP_H_ #include "dsexceptions.h" #include "vector.h" // BinaryHeap class // CONSTRUCTION: wi

Write Your Message!

Captcha
Free Assignment Quote

Assured A++ Grade

Get guaranteed satisfaction & time on delivery in every assignment order you paid with us! We ensure premium quality solution document along with free turntin report!

All rights reserved! Copyrights ©2019-2020 ExpertsMind IT Educational Pvt Ltd