Monitoring usage

A possible first step in optimizing memory management is to identify how memory is being used, and where.

What we need to do is keep track, for each type (name) and number allocated:

We can also gather statistics for the overall memory usage, including keeping track of number of objects allocated and freed, and total memory used.

First, lets see an API for measuring usage:

	/* usage_xmemory.h */
	/* call every time  elements of  are alloc'd, free'd */
	void alloc_usage_xmemory(const char * type_name, int num);
	void free_usage_xmemory(const char * type_name, int num);

	/* reset maximums; this is useful if we want to keep
	 * track of maximum used between points
	void reset_max_usage_xmemory(const char * type_name, int num);

	/* dump the statistics to a file */
	void dump_usage_xmemory(FILE * fp);

We can leverage the xalloc()/xfree() macros introduced in a previous section to insert this usage monitoring code project wide.

	#define xalloc(_t)	\
		( alloc_usage_xmemory(#_t, 1),\
		  (_t*)malloc(sizeof(_t) )
	#define xnalloc(_t, _n)	\
		( alloc_usage_xmemory(#_t, (_n)),\
		  (_t*)malloc((_n)*sizeof(_t) )
	#define xfree(_t, _p)	\
		( free_usage_xmemory(#_t, 1),\
		  free(_t) )
	#define xnfree(_t, _n, _p)	\
		( free_usage_xmemory(#_t, (_n)),\
		  free(_t) )

For the sample implementation shown below the output that is dumped to a file will look like:

	struct T:1:100:0:100

The first row should be read as: arrays of 12 char were allocated 480 times, freed 433 times, with a peak of 90 such arrays in use. We can also figure out that at the time dump_usage_xmemory() was called 47 arrays were still in use.

The output is separated by : so that it can be processed by programs such as sort. We can use them to help us analyze where we need to rework memory management.

Here is a sample implementation. It is stripped down from what it would be in real life. In particular, no global statistics are collected.

	/* usage_xmemory-private.h OR usage_xmemory.c */

	typedef struct cell_usage_xmemory * cell_usage_xmemory;
	struct cell_usage_xmemory {
	  cell_usage_xmemory *	cux_next;
	  char *		cux_name;
	  int			cux_num;
	  /* these may need to be long long */
	  unsigned		cux_allocd;
	  unsigned		cux_freed;
	  unsigned		cux_max;
	/* as well as the usual accessor macros */

Now we shall show the implementation. This example uses a list for storing all type/number pairs. The real implementation would use a hash table.

	static cell_usage_xmemory	lcl_list_usage_xmemory = 0;

	static cell_usage_xmemory
		const char *	type_name,
		int		num
	  cell_usage_memory	cell;

	  for( cell = lcl_list_usage_xmemory; cell != 0;
	  	cell = next_cell_usage_xmemory(cell)) {
	    if( num == num_cell_usage_xmemory(cell) &&
	    	strcmp(type_name, name_cell_usage_xmemory(cell)) == 0 ) {
	      return cell;
	  return 0;

		const char *	type_name,
		int		num
	  cell_usage_xmemory	cell = get_usage_xmemory(type_name, num);
	  int			len;
	  char *		str;
	  int			curr;

	  if( cell == 0 ) {
	    /* Note malloc() is used; if we used xalloc,
	     * we would get infinite recursion (see above)

	    cell = malloc(sizeof(struct cell_usage_xmemory));
	    len = strlen(type_name);
	    str = malloc(sizeof(len)+1);
	    strcpy(p, type_name);

	    next_cell_usage_xmemory(cell) = lcl_list_usage_xmemory;
	    lcl_list_usage_xmemory = cell;
	    num_list_usage_xmemory(cell) = num;
	    name_cell_usage_xmemory(cell) = str;
	    allocd_cell_usage_xmemory(cell) = 0;
	    freed_cell_usage_xmemory(cell) = 0;
	    max_cell_usage_xmemory(cell) = 0;

	  curr = allocd_cell_usage_xmemory(cell) - freed_cell_usage_xmemory(cell);
	  if( curr > max_cell_usage_xmemory(cell) ) {
	    max_cell_usage_xmemory(cell) = curr;

		const char *	type_name,
		int		num
	  cell_usage_xmemory	cell = get_usage_xmemory(type_name, num);

	  assert( cell );


		FILE *		fp
	  for( cell = lcl_list_usage_xmemory; cell != 0;
	  	cell = next_cell_usage_xmemory(cell)) {
	    fprintf(fp, "%s:%d:%u:%u:%u\n",

Next Prev Main Top Feedback