Thread: free memory in structure

  1. #16
    Registered User
    Join Date
    Dec 2004
    Posts
    163
    ok, thanks for the advice guys!

  2. #17
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Catching the bad_alloc exception new throws to free some memory is not a good idea. To do that properly, you would have to wrap every call to new with a try...catch, which is worse even than the constant NULL checking in C.

    The correct way to do this is to install a new handler. Call set_new_handler() and pass a function that can attempt to, for example, free some memory by writing an in-memory cache to disk and freeing the memory there.

    Scott Meyers describes new handlers in detail in Item 49 of Effective C++, 3rd edition, but I'll give you a short synopsis here.

    The function operator new() is (or should be) implemented roughly like this:
    Code:
    void * operator new(size_t bytes)
    {
      if(bytes == 0) bytes = 1;
      while(true) {
        void *mem = __real_allocate(bytes);
        if(mem) {
          return mem; // Success.
        }
        // Allocation failed, call new handler if available.
        new_handler handler = __get_new_handler();
        if(handler) {
          (*handler)();
        } else {
          throw bad_alloc();
        }
      }
    }
    As you can see, new will go into an infinite loop until either it gets enough memory or something somehow breaks the loop. Knowing this, there are 5 things a new handler may legally do:
    • Make memory available so that the next real allocation will succeed.
    • Install a different new handler, so that the next iteration will call that one instead.
    • Deinstall the new handler by calling set_new_handler(0), so that the next iteration will throw.
    • Throw an exception. By convention (and perhaps standard requirement, not sure) this exception must be of type bad_alloc or a class derived from it.
    • Never return, by calling abort(), exit() or similar.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  3. #18
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Quote Originally Posted by CornedBee
    Catching the bad_alloc exception new throws to free some memory is not a good idea. To do that properly, you would have to wrap every call to new with a try...catch, ....
    That's not precisely true; the standard gives some tools to avoid doing that all the time.

    If the dynamic memory allocation is managed by a classes it is not necessary to wrap every usage of operator new in try/catch ..... the compiler handles that for you. For example;
    Code:
    // include necessary headers
    class MemoryManager
    {
         public:
              MemoryManager(int x);
              ~MemoryManager();
         private:
              char *character_memory;
              double *double_memory;
    };
    
    MemoryManager::MemoryManager(int x) : character_memory(new char [x]), double_memory(new double [x])
    {}
    
    MemoryManager::~MemoryManager()
    {
        delete [] character_memory;
        delete [] double_memory;
    }
    
    int main()
    {
        bool want_to_retry = true;
        while (want_to_retry)
        {
            try
           {
                MemoryManager mem;
                  // do something funky with mem
    
                want_to_retry = false;
           }
           catch (std::bad_alloc &)
           {
               // no memory leak if either memory allocation failed
               
               // do something to reduce memory usage so operator new might succeed
           }
        }
    }
    The C++ standard guarantees that, if either usage of operator new fails, that the memory will be properly cleaned up.

    If that approach seems like too much hard work, the standard library comes to the rescue : for example, it is also possible to employ auto_ptr (to look after single objects allocated with operator new) and standard containers (which manage dynamically allocated memory behind the scenes). So;
    Code:
    // include necessary headers
    int main()
    {
        bool want_to_retry = true;
        while (want_to_retry)
        {
            try
           {
        
               std::auto_ptr<SomeType> mem(new SomeType());
               std::auto_ptr<SomeOtherType> moremem(new SomeOtherType());
    
               std::vector<YetAnotherType>  array(25);
    
               // do something funky with mem, moremem, or array
    
               want_to_retry = false;
           }
           catch (std::bad_alloc &)
           {
               // no memory leak if either memory allocation failed
               
               // do something to reduce memory usage so operator new might succeed
           }
        }
    }
    If any of the allocations should fail, all allocated memory that has been successfully allocated dynamically will be released. So, if a bad_alloc exception is caught, it does not need to worry about cleaning up the mess caused by the preceding code --- it only has to somehow release other resources in an attempt to ensure the dynamic memory allocation will succeed. The only assumption that needs to be made is that the types (SomeType, SomeOtherType, YetAnotherType) implementation does not cause a memory leak.

  4. #19
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    I'm not talking about memory leaks. I'm talking simply about knowing where you stopped. The code below has a great potential for bugs.
    Code:
    while(!done) {
      try {
        initial_mem = new memory;
        // do some stuff
        if(some condition) {
          more_mem = new memory(generated data);
          // do more stuff
        } else {
          different_mem = new memory(other generated data);
          // do different stuff
        }
        final_mem = new memory(more generated data);
        // do final stuff
      } catch(std::bad_alloc &) {
        // Free some memory and retry.
      }
    }
    How do you know which allocation failed? How do you know which work you've already done? How do you avoid doing the same work twice, incrementing a global variable twice?
    As you see, even if all _mem variables are smart pointers and there are no leaks, having one catch for all allocations simply might not be sufficient. Keep in mind that the generated data I mention as constructor arguments enforce sequential dependency: in my scenario, it is simply not possible to allocate the memory any earlier. (And if it was, it would still be bad design.)

    That's why the new_handler is simply a much cleaner and better solution.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  5. #20
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    I disagree. You are using a new_handler to work around issues that arise with a problematic design. Designs that require tight coupling between parts tend to introduce problems when something goes wrong in one of the parts (eg in your case, a failed memory allocation generates a need to decrement a global variable). It takes more effort, but minimising coupling between components of a system would allow easier recovery should a memory allocation fail (eg it is not necessary to know which allocation failed; it is only necessary to know that one of a specific set of allocations failed).

    Interestingly enough, the most common justification I see for tightly coupled design is "performance". And, I have yet to see an example where one of the benefits of a tightly coupled design is actually increased performance; on the contrary, because of all the additional checks that are made necessary by the coupling, there is generally reduced performance, lower tolerance to errors, and often a maintenance nightmare. Which makes me consider that a tightly coupled design is more often the result of incomplete analysis than anything else.

  6. #21
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Quote Originally Posted by grumpy
    I disagree. You are using a new_handler to work around issues that arise with a problematic design. Designs that require tight coupling between parts tend to introduce problems when something goes wrong in one of the parts (eg in your case, a failed memory allocation generates a need to decrement a global variable). It takes more effort, but minimising coupling between components of a system would allow easier recovery should a memory allocation fail (eg it is not necessary to know which allocation failed; it is only necessary to know that one of a specific set of allocations failed).
    Uh, what? I think you totally misunderstood my point.

    The truth is that using try...catch comes from an incomplete understanding of the language's and library's capabilities and thus a choice of the wrong tool.

    In short, by using a new_handler, the allocation doesn't fail in the first place (from the view of the code calling new), thus you get better code separation (no handling of insufficient memory in the algorithm that has nothing to do with that) and less coupling between components (the memory management, in this case freeing of some temporary space, and everything else).
    It is not my case where I need to decrement a global variable. In my case, the algorithm just works and the issue I pointed out never arises. It is your code where you might or might not have to decrement a temporary or skip some code or do something else IF the memory allocation fails.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  7. #22
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    >> Catching the bad_alloc exception new throws to free some memory is not a good idea. To do that properly, you would have to wrap every call to new with a try...catch.

    I won't argue with the good information you provided for set_new_handler, but in the OP's case I think using try/catch is likely appropriate.

    First, I am skeptical about whether there is a real issue or just the fear of an issue. However, assuming the OP really does have a specific situation where specific code is likely to fail during allocation, and some other specific code is available that can make successful allocation more likely, then try/catch would be appropriate. I'm not talking about a global memory error handling strategy, which would be better but as I said is tricky and likely beyond the OP's current capabilities. I'm talking about handling the single specific case. In that situation, you would only place the try block around the single call to allocate memory that is likely to fail.

  8. #23
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    If it is a single isolated allocation, yes. But that's not how I understood the OP's question (hidden down in the middle of the first page).

    But I also agree with you that the OP doesn't seem to have a specific issue. Experience shows that usually, you won't have any memory to free.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Mutex and Shared Memory Segment Questions.
    By MadDog in forum Linux Programming
    Replies: 14
    Last Post: 06-20-2010, 04:04 AM
  2. help understanding free and memory leaks
    By mc61 in forum C Programming
    Replies: 4
    Last Post: 04-08-2008, 12:47 AM
  3. Assignment Operator, Memory and Scope
    By SevenThunders in forum C++ Programming
    Replies: 47
    Last Post: 03-31-2008, 06:22 AM
  4. Memory leak with detached pthreads - how to free?
    By rfk in forum Linux Programming
    Replies: 2
    Last Post: 08-17-2007, 06:50 AM
  5. OS out of memory if malloc without free?
    By hkuser2001 in forum C Programming
    Replies: 7
    Last Post: 04-24-2006, 07:23 AM