jeudi 29 avril 2021

Memory leak after moving a std::string with placement new

I have a structure that contains a string and that structure is used in a vector. When the vector grows, all of the elements are moved to the new allocation. Unfortunately, this move also results in std::string leaking memory.

Here are a few minimum reproducible cases. This first example will illustrate where a memory leak occurs but sense can be made of it. The second example will cover the memory leak that has stumped me. The third will take it a step further.

int main(void)
{
  char* allocation = new char[sizeof(std::string)];
  std::string start("start");
  std::string* move = (std::string*)allocation;
  new (move) std::string(std::move(start));
  delete[] allocation;
}

Unsurprisingly, this results in a memory leak. The start string is constructed and memory should be allocated for the data. That string is then moved into move and remains there for the rest of the program. Because it has been moved, the destructor for move will not be called and this will result in a memory leak like the one below. The reason I am creating a character array is to avoid the constructor and destructor calls for the move string.

Detected memory leaks!
Dumping objects ->
{207} normal block at 0x00000278D16848C0, 16 bytes long.
 Data: <  g x           > D0 D7 67 D1 78 02 00 00 00 00 00 00 00 00 00 00
Object dump complete.

An interesting thing to note here is that the data < g x > does not contain the string used to initialize start, which probably indicates that this leak is not coming from the string`s buffer.

Now we'll add one line to the previous example in an attempt to remove the memory leak.

int main(void)
{
  char* allocation = new char[sizeof(std::string)];
  std::string start("start");
  std::string* move = (std::string*)allocation;
  new (move) std::string(std::move(start));
  start = std::move(*move); // The new line.
  delete[] allocation;
}

Now, instead of that string remaining in move until the end of the program, it is moved back into start and since start's destructor should be called, the string's allocation should be freed. However, when I run this, I still experience a similar memory leak.

Detected memory leaks!
Dumping objects ->
{207} normal block at 0x000001AE813FECC0, 16 bytes long.
 Data: <  @             > 80 15 40 81 AE 01 00 00 00 00 00 00 00 00 00 00
Object dump complete.

I also attempted this test with a single string allocation rather than allocating a character array and I still experience a similar memory leak with the code below.

int main(void)
{
  std::string start("start");
  std::string* move = new std::string;
  new (move) std::string(std::move(start));
  start = std::move(*move);
  delete move;
}

Why would this memory leak happen? Is there something about std::string that I am missing here or is my use of placement new somehow the culprit?

Additional Notes: Memory leak in placement new of standard library string - This question only covers my first example. It does not explain or cover the last two.

Aucun commentaire:

Enregistrer un commentaire