Almost every developer who has used the C programming language has at some point sighed at the daunting task of managing memory usage. Allocating enough memory to use, keeping track of those allocations, freeing the memory when you no longer need it—these tasks can be quite complex. And of course, failure to do those things properly can result in a program that crashes itself, or worse, crashes the computer. Fortunately, the APR library that Subversion depends on for portability provides the apr_pool_t type, which represents a pool from which the application may allocate memory.
A memory pool is an abstract representation of a chunk of
      memory allocated for use by a program.  Rather than requesting
      memory directly from the OS using the standard
      malloc() and friends, programs that link
      against APR can simply request that a pool of memory be created
      (using the apr_pool_create() function).
      APR will allocate a moderately sized chunk of memory from the
      OS, and that memory will be instantly available for use by the
      program.  Any time the program needs some of the pool memory, it
      uses one of the APR pool API functions, like
      apr_palloc(), which returns a generic
      memory location from the pool.  The program can keep requesting
      bits and pieces of memory from the pool, and APR will keep
      granting the requests.  Pools will automatically grow in size to
      accommodate programs that request more memory than the original
      pool contained, until of course there is no more memory
      available on the system.
Now, if this were the end of the pool story, it would hardly
      have merited special attention.  Fortunately, that's not the
      case.  Pools can not only be created; they can also be cleared
      and destroyed, using apr_pool_clear() and
      apr_pool_destroy() respectively.  This
      gives developers the flexibility to allocate several—or
      several thousand—things from the pool, and then clean up
      all of that memory with a single function call!  Further, pools
      have hierarchy.  You can make “subpools” of any
      previously created pool.  When you clear a pool, all of its
      subpools are destroyed; if you destroy a pool, it and its
      subpools are destroyed.
Before we go further, developers should be aware that they
      probably will not find many calls to the APR pool functions we
      just mentioned in the Subversion source code.  APR pools offer
      some extensibility mechanisms, like the ability to have custom
      “user data” attached to the pool, and mechanisms
      for registering cleanup functions that get called when the pool
      is destroyed.  Subversion makes use of these extensions in a
      somewhat non-trivial way.  So, Subversion supplies (and most of
      its code uses) the wrapper functions
      svn_pool_create(),
      svn_pool_clear(), and
      svn_pool_destroy().
While pools are helpful for basic memory management, the pool construct really shines in looping and recursive scenarios. Since loops are often unbounded in their iterations, and recursions in their depth, memory consumption in these areas of the code can become unpredictable. Fortunately, using nested memory pools can be a great way to easily manage these potentially hairy situations. The following example demonstrates the basic use of nested pools in a situation that is fairly common—recursively crawling a directory tree, doing some task to each thing in the tree.
Example 8.5. Effective Pool Usage
/* Recursively crawl over DIRECTORY, adding the paths of all its file
   children to the FILES array, and doing some task to each path
   encountered.  Use POOL for the all temporary allocations, and store
   the hash paths in the same pool as the hash itself is allocated in.  */
static apr_status_t 
crawl_dir (apr_array_header_t *files,
           const char *directory,
           apr_pool_t *pool)
{
  apr_pool_t *hash_pool = files->pool;  /* array pool */
  apr_pool_t *subpool = svn_pool_create (pool);  /* iteration pool */
  apr_dir_t *dir;
  apr_finfo_t finfo;
  apr_status_t apr_err;
  apr_int32_t flags = APR_FINFO_TYPE | APR_FINFO_NAME;
  apr_err = apr_dir_open (&dir, directory, pool);
  if (apr_err)
    return apr_err;
  /* Loop over the directory entries, clearing the subpool at the top of
     each iteration.  */
  for (apr_err = apr_dir_read (&finfo, flags, dir);
       apr_err == APR_SUCCESS;
       apr_err = apr_dir_read (&finfo, flags, dir))
    {
      const char *child_path;
      /* Clear the per-iteration SUBPOOL.  */
      svn_pool_clear (subpool);
      /* Skip entries for "this dir" ('.') and its parent ('..').  */
      if (finfo.filetype == APR_DIR)
        {
          if (finfo.name[0] == '.'
              && (finfo.name[1] == '\0'
                  || (finfo.name[1] == '.' && finfo.name[2] == '\0')))
            continue;
        }
      /* Build CHILD_PATH from DIRECTORY and FINFO.name.  */
      child_path = svn_path_join (directory, finfo.name, subpool);
      /* Do some task to this encountered path. */
      do_some_task (child_path, subpool);
      /* Handle subdirectories by recursing into them, passing SUBPOOL
         as the pool for temporary allocations.  */
      if (finfo.filetype == APR_DIR)
        {
          apr_err = crawl_dir (files, child_path, subpool);
          if (apr_err)
            return apr_err;
        }
      /* Handle files by adding their paths to the FILES array.  */
      else if (finfo.filetype == APR_REG)
        {
          /* Copy the file's path into the FILES array's pool.  */
          child_path = apr_pstrdup (hash_pool, child_path);
          /* Add the path to the array.  */
          (*((const char **) apr_array_push (files))) = child_path;
        }
    }
  /* Destroy SUBPOOL.  */
  svn_pool_destroy (subpool);
  /* Check that the loop exited cleanly. */
  if (apr_err)
    return apr_err;
  /* Yes, it exited cleanly, so close the dir. */
  apr_err = apr_dir_close (dir);
  if (apr_err)
    return apr_err;
  return APR_SUCCESS;
}
The previous example demonstrates effective pool usage in
      both looping and recursive situations.
      Each recursion begins by making a subpool of the pool passed to
      the function.  This subpool is used for the looping region, and
      cleared with each iteration.  The result is memory usage is
      roughly proportional to the depth of the recursion, not to total
      number of file and directories present as children of the
      top-level directory.  When the first call to this recursive
      function finally finishes, there is actually very little data
      stored in the pool that was passed to it.  Now imagine the extra
      complexity that would be present if this function had to
      alloc() and free()
      every single piece of data used!
Pools might not be ideal for every application, but they are extremely useful in Subversion. As a Subversion developer, you'll need to grow comfortable with pools and how to wield them correctly. Memory usage bugs and bloating can be difficult to diagnose and fix regardless of the API, but the pool construct provided by APR has proven a tremendously convenient, time-saving bit of functionality.