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

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

Program to calculation of mortgage interest rates, This assignment builds o...

This assignment builds on Homework 3. The two major modifications are the instruction of pointers and the calculation of mortgage interest rates. Requirements for Project 2:

Should my constructors employ"assignment"or"initialization, Should my const...

Should my constructors employ "assignment" or "initialization lists"?

Equation, My project is compiling but the equation entered is not working

My project is compiling but the equation entered is not working

C++ programming, Write a program to convert English units to metric (e.g., ...

Write a program to convert English units to metric (e.g., miles to kilometers, gallons to liters, etc.). Include a specification and a code design.

Create a client program tracker, This project simulates an application call...

This project simulates an application called tracker for the Department of Transportation (DOT) in which highway traffic data is accumulated in real time using various sensing equi

Assignment help, It''s that spooky time of year when the dead walk the Eart...

It''s that spooky time of year when the dead walk the Earth, things go bump in the night, and the Centers for Disease Control (CDC) recognizes the need for zombie preparedness (

Explain multiple inheritance, Multiple Inheritance Multiple inheritance ,...

Multiple Inheritance Multiple inheritance , as the name suggests , is  deriving a class from more than one class . The derived class inherits all the properties of all its base c

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