tags:

views:

222

answers:

6

If my application runs out of memory, I would like to re-run it with changed parameters. I have various malloc-s/new-s in various parts of the application, the sizes of which are not known in advance. I see two options:

  • Track all memory allocations and write a restarting procedure which deallocates all before re-running with changed parameters. (Of course, I free memory at the appropriate places if no errors occur)
  • Restarting the application (e.g., with WinExec() on Windows) and exiting

I am not thrilled by either solution. Did I miss an alternative maybe.

Thanks

+5  A: 

You could embedd all the application functionality in a class. Then let it throw an expection when it runs out of memory. This exception would be catched by your application and then you could simply destroy the class, construct a new one and try again. All in one application in one run, no need for restarts. Of course this might not be so easy, depending on what your application does...

PeterK
this will deallocate the dynamically allocated memory in methods of that class?
Frank
@Frank assuming that your classes have destructors that free the memory they allocated, or preferably that you're using say `shared_ptr`
Mark B
+2  A: 

There is another option, one I have used in the past, however it requires having planned for it from the beginning, and it's not for the library-dependent programmer:

Create your own heap. It's a lot simpler to destroy a heap than to cleanup after yourself.

Doing so requires that your application is heap-aware. That means that all memory allocations have to go to that heap and not the default one. In C++ you can simply override the static new/delete operators which takes care of everything your code allocates, but you have to be VERY aware of how your libraries, even the standard library, use memory. It's not as simple as "never call a library method that allocates memory". You have to consider each library method on a case-by-case basis.

It sounds like you've already built your app and are looking for a shortcut to memory wiping. If that is the case, this will not help as you could never tack this kind of thing onto an already built application.

Tergiver
I though about that also, but was held back the prospect of managing the "holes" is memory, e.g., if the code requests A, B, C, and then frees B. The code would probably benefit quite a bit from a proper own heap management. Did you allocate a large chunk of memory to your heap right at the start of your application, or in multiple parts?
Frank
Heap fragmentation is no different between a heap you get from HeapCreate and that of the default heap. You can specify that the heap you create is a low-fragmentation heap (LFH) by calling HeapSetInformation which helps to reduce fragmentation with small requests allocating larger blocks, but again this is no different than how the default heap works.
Tergiver
+2  A: 

A way to accomplish this:

Define an exit status, perhaps like this:

static const int OUT_OF_MEMORY=9999;

Set up a new handler and have it do this:

exit(OUT_OF_MEMORY);

Then just wrap your program with another program that detects this exit status. When it does then it can rerun the program.

Granted this is more of a workaround than a solution...

The wrapper program I mentioned above could be something like this:

static int special_code = 9999;
int main()
{
   const char* command = "whatever"; 
   int status = system(command);
   while ( status == 9999 )
   {
      command = ...;
      status = system(command);
   }
   return 0;
}

That's the basicness of it. I would use std::string instead of char* in production. I'd probably also have another condition for breaking out of the while loop, some maximum number of tries perhaps.

Whatever the case, I think the fork/exec route mentioned below is pretty solid, and I'm pretty sure a solution like it could be created for Windows using spawn and its brethren.

Craig W. Wright
nice workaround
Yassin
Interesting. Could you provide some guidance as to what to put in the wrapper program? It would need to be in a separate executable, right?
Frank
On Linux, you might also want to detect when the program is killed with a `SIGKILL`, because that's the signal the out of memory process killer uses to kill your program and you *don't* get to define the exit code in that case. (OTOH, this does prevent you from using `kill -9` to kill your own program.)
Ken Bloom
A: 

simplicity rules: just restart your app with different parameters.

it is very hard to either track down all allocs/deallocs and clean up the memory (just forget some minor blocks inside bigger chunks [fragmentation] and you still have problems to rerun the class), or to do introduce your own heap-management (very clever people have invested years to bring nedmalloc etc to live, do not fool yourself into the illusion this is an easy task).

so:

  • catch "out of memory" somehow (signals, or std::bad_alloc, or whatever)
  • create a new process of your app:
    • windows: CreateProcess() (you can just exit() your program after this, which cleans up all allocated resources for you)
    • unix: exec() (replaces the current process completely, so it "cleans up all the memory" for you)
  • done.
akira
+2  A: 

The wrapper-program (as proposed before) does not need to be a seperate executable. You could just fork, run your program and then test the return code of the child. This would have the additional benefit, that the operating system automatically reclaims the child's memory when it dies. (at least I think so)

Anyway, I imagined something like this (this is C, you might have to change the includes for C++):

#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

#define OUT_OF_MEMORY 99999 /* or whatever */

int main(void)
{
    int pid, status;

fork_entry:
    pid = fork();
    if (pid == 0) {
        /* child - call the main function of your program here */
    } else if (pid > 0) {
        /* parent (supervisor) */
        wait(&status); /* waiting for the child to terminate */
        /* see if child exited normally
           (i.e. by calling exit(), _exit() or by returning from main()) */
        if (WIFEXITED(status)) {
            /* if so, we can get the status code */
            if (WEXITSTATUS(status) == OUT_OF_MEMORY) {
                /* change parameters */
                goto fork_entry; /* forking again */
            }
        }
    } else {
            /* fork() error */
            return 1;
    }
    return 0;
}

This might not be the most elegant solution/workaround/hack, but it's easy to do.

RWS
Danke nach Leipzig! My application is Windows-only currently. Will try to identify the corresponding methods in the Windows API.
Frank
My fault, I thought Windows had these basic POSIX routines. Well, I don't know a thing about Windows, really.
RWS
A: 

Be warned that on Linux, by default, your program can request more memory than the system has available. (This is done for a number of reasons, e.g. avoiding memory duplication when fork()ing a program into two with identical data, when most of the data will remain untouched.) Memory pages for this data won't be reserved by the system until you try to write in every page you've allocated.

Since there's no good way to report this (since any memory write can cause your system to run out memory), your process will be terminated by the out of memory process killer, and you won't have the information or opportunity for your process to restart itself with different parameters.

You can change the default by using the setrlimit system call, to to limit the RLIMIT_RSS which limits the total amount of memory your process can request. Only after you have done this will malloc return NULL or new throw a std::bad_alloc exception when you reach the limit that you have set.

Be aware that on a heavily loaded system, other processes can still contribute to a systemwide out of memory condition that could cause your program to be killed without malloc or new raising an error, but if you manage the system well, this can be avoided.

Ken Bloom