hashmap.c icon indicating copy to clipboard operation
hashmap.c copied to clipboard

how to free mem?

Open sunzeping001 opened this issue 2 years ago • 1 comments

i found that hashmap_free do not work, the mem can not free and with the time going on, the mem will reach very high, so what can i do?

sunzeping001 avatar Jan 03 '24 04:01 sunzeping001

TLDR

If you are allocating memory to your struct item (using malloc or related functions), you can free that memory after you call hashmap_set(hashmap, item).

At the end of this comment, I've included a source code with its output, for reference.

Long Version

I looked at the hashmap implementation, I'll try to explain what I've understood.

Hashmap Memory Structure

Hashmap Structure

  • Memory allocated to the hashmap is: sizeof(struct hashmap) + (2 * map->bucketsz)) map->bucketsz = sizeof(struct bucket) + sizeof(struct item), where item is the struct you want to insert into the hashmap.

  • The reason why an additional 2 * map->bucketsz space is allocated to the hashmap is because it is used internally by the hashmap when their functions get called. One is for temporary storage hash->spare (like how a temp variable is used when you want to swap 2 variables), and another hash->entry, for initially storing the item when hashmap_set() is called.

  • The bucket_item struct (refer to diagram, this is something I named - it is not in the source code), holds a struct bucket (containing the hash) and struct item (your item struct).

  • The memory allocated to hashmap->buckets is equal to n bucket_items, where n = hashmap->nbuckets.

Inserting an item into the hashmap

  1. When hashmap_set() or hashmap_set_with_hash() is called, the bucket hash is calculated. The bucket structure and your item struct get copied (using memcpy) into hashmap->edata. (This is the point where you can free memory allocated to item, because it was copied).

  2. The bucket index is calculated by taking the some LSB bits of the hash (the number of bits depends on map->mask, which is map->nbuckets - 1). This the bucket_item where the item will be stored, i.e., copied into the bucket_item from the entry bucket. PS: I won't be explaining what happens when a collision occurs, I just wanted to explain that item gets copied into one of the bucket_items.

What happens when hashmap_free() or hashmap_clear() is called?

  1. For every bucket, hashmap->elfree() is called, which is the free function passed to hashmap_create(). NOTE: If you have allocated memory to your item before inserting to the hashmap (hashmap_set() or hashmap_set_with_hash()), this memory will not get freed when hashmap_free() or hashmap_clear() is called. It only frees the item struct present in the bucket_item.

  2. Memory to hashmap->buckets and hashmap get freed.

Example code and output which does not result in memory leak

Source code: https://p.ip.fi/9jq6 Valgrind output:

$ valgrind --leak-check=full build/main
==6395== Memcheck, a memory error detector
==6395== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==6395== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==6395== Command: build/main
==6395==
string: 123456789
==6395==
==6395== HEAP SUMMARY:
==6395==     in use at exit: 0 bytes in 0 blocks
==6395==   total heap usage: 5 allocs, 5 frees, 1,650 bytes allocated
==6395==
==6395== All heap blocks were freed -- no leaks are possible
==6395==
==6395== For counts of detected and suppressed errors, rerun with: -v
==6395== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Sorry for the long explanation, I probably explained a lot of stuff unrelated to the issue.

tvcv901 avatar Apr 03 '24 13:04 tvcv901