Solidity Memory Management – Does ‘Delete’ Free Mapping Space

arraysgo-ethereummappingmemorysolidity

I have the following mapping(uint => Foo[]) data structure. I have mapped 10 unique ids with 10 Foo structs, where each Foo is an arraylist of 10 items.

Step 1:

for(int i = 0; i < 10; i++)
   for(int j = 0; j < 10; j++)
       add(i, j); 

Step 2: I delete the items pushed before. I'm not sure which one is the correct approach among a) and b) below. Approach b) leaves holes by only resetting the members in the struct

a)  for(int i = 0; i < 10; i++)      | b) for(int i = 0; i < 10; i++)
           delete(i);                |      for(int j = 0; j < 10; j++)
                                     |          delete_items(i, j)  

Step 3: I push new items with different mapping uint id.

for(int i = 20; i < 30; i++)
   for(int j = 20; j < 30; j++)
       add(i, y); 

[Q] Would delete mapping_array[id] actually free the memory that was allocated by the array list within the mapping? Or would it only zero out the memory as if delete was used on Array List, and the item remains in the mapping as empty, but still taking memory space?

[Q] In total, I have mapped 10 unique ids with 10 Foo structs, where each Foo is an arraylist of 10 items. Later I delete the items in my mapping data structure then insert new items into it. So would the newly inserted items get new memory allocated for them, or would they be overwritten in memory of recently deleted items?

These matter because I am using a mapping data structure of very large size and items get inserted and removed frequently. I know delete won’t free memory on an Array, but not sure if it’s the same in mapping.

I am using the following code piece from this question's answer: https://ethereum.stackexchange.com/a/1458/4575

  struct Foo{
    uint x;
  }
  mapping(uint => Foo[]) mapping_array;

  function add(uint id, uint _x) public {
    mapping_array[id].push(Foo(_x));
  }

  function get(uint id, uint index) public returns(uint){
    return mapping_array[id][index].x;
  }

  function delete_(uint id) public {
    delete mapping_array[id];
  }

  function delete_items(uint id, uint index) public {
    delete mapping_array[id][index];
  }

Delete:

delete a assigns the initial value for the type to a. I.e. for integers it is equivalent to a = 0, For structs, it assigns a struct with all members reset.

delete has no effect on whole mappings (as the keys of mappings may be arbitrary and are generally unknown). So if you delete a struct, it will reset all members that are not mappings and also recurse into the members unless they are mappings. However, individual keys and what they map to can be deleted.

It is important to delete a really behaves like an assignment to a, i.e. it stores a new object in a.

Link: http://solidity.readthedocs.io/en/develop/types.html

Thank you for your valuable time and help.

Best Answer

Based on my Debug observation:

I have tested that:

  • Firstly I insert data into my map data-structure as I explained in my question.
  • Secondly, I have returned the address of first mapped index, which is 0, this could be done for other indexes as well (0:10).
  • Without a delete operation I can retrieve the data perfectly fine from the address of mapped Foo[]. But if I did the delete operation, Solidity gives an error.

This debug result: leads me to conclude that memory is freed or address does not contain any valid information.

Test code:

set_txn_hash     = my_contract.transact().map_insert();
contract_address = unmigrated_chain.wait.for_receipt(set_txn_hash)

for x in range(0, 9):
   for y in range(0, 9):
         output           = my_contract.call().get_(x, y);
         print(output);  //returns correct value.

set_txn_hash     = my_contract.transact().map_remove_map();                                             
contract_address = unmigrated_chain.wait.for_receipt(set_txn_hash)                                      

set_txn_hash     = my_contract.transact().get_map_address(0);
contract_address = unmigrated_chain.wait.for_receipt(set_txn_hash)

output           = my_contract.call().get_map();
print(output);

output           = my_contract.call().try_();
print(output);  //Solidity gives error if delete map_remove_map() is called

Additional functions inside my Contract:

  function map_insert(){
    for(uint i = 0; i < 10; i++)
      for(uint j = 0; j < 10; j++)
        add(i, i*10 + j);
  }

  function map_remove_map(){
    for(uint i = 0; i < 10; i++)
      delete_(i);
  }

  function map_remove_map_item(){
    for(uint i = 0; i < 10; i++)
      for(uint j = 0; j < 10; j++)
        delete_items(i, j);
  }

  function get_map_address(uint id) {
    uint addr;
    Foo[] a = foo[id];

    assembly{
    addr := a
        }
    map_address = addr;
  }

  function get_map() constant returns (uint){
    return map_address;
  }

  function try_() constant returns (uint ){
    uint addr = map_address;
    Foo[] a;
    assembly{
    a := addr
        }
    return a[1].x;  
  }