-1

I'm working on a project, and need a char buffer to send a struct over a network. I was using:

char* buf = new char[sizeof(obj)];

Later, someone told me that I would be leaking memory if I did it that way without deleting. I debugged my program with Visual Studio 2017 Professional, and my memory sat constant, no matter how many times I made this buffer.

My question is this: did C++17 fix the memory leak issues that the 'new' operator could cause, or is this something specific to the compiler I'm using? Thanks in advance!

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
user29451
  • 25
  • 5
  • You didn't leak memory, you leaked address space. If you wanna leak memory, try `for(;;) new char;` – Joshua Oct 20 '20 at 19:13
  • And by the way this is not a bug, so there is no "fixing" involved. – spectras Oct 20 '20 at 19:14
  • @Joshua Never heard the term "leaked address space" before, what does it mean? Looks like a regular memory leak to me. – HolyBlackCat Oct 20 '20 at 19:14
  • @HolyBlackCat: If you allocate large chunks and don't write to it, the memory isn't actually allocated yet, only the address space is. Large chunks are allocated by memory mapping. – Joshua Oct 20 '20 at 19:15
  • OP, are you thinking about [this obscure way of leaking memory](https://stackoverflow.com/q/53870522/2752075) that was fixed in C++17? C++17 didn't eliminate *all* memory leaks (how could it do that?), just one of the obscure sources of them. – HolyBlackCat Oct 20 '20 at 19:16
  • 1
    @HolyBlackCat There is no difference between the two in C++ as far as I know. From what I understand, the difference may exist on platforms that do not commit memory until the first write. So until you try to access the object, that address is reserved but no memory for it is committed to your process yet. These platforms are incompatible with C++ but pretend otherwise. – François Andrieux Oct 20 '20 at 19:16
  • @Joshua That is an implementation detail and is incompatible with C++. Some Linux installations work this way but they are non-conforment. – François Andrieux Oct 20 '20 at 19:17
  • @FrançoisAndrieux: It's an implementation detail that OP already observed. – Joshua Oct 20 '20 at 19:18
  • @FrançoisAndrieux I see. Heard something about it happening on Linux, but apparently Windows does it too. Was able to reproduce on MinGW. – HolyBlackCat Oct 20 '20 at 19:21
  • @Joshua Windows does not have this behavior. Unless they are cross compiling, this is not what is happening. Edit : Can't speak for MinGW though... Edit 2 : I suspect it is more likely the method of verifying total memory usage is incorrect, that the array is actually cleaned up or even that the number of leaks observed has not been enough to require a new page. – François Andrieux Oct 20 '20 at 19:21
  • @HolyBlackCat That may have been what I was thinking of, and confused it with normal memory leaks, thank you! – user29451 Oct 20 '20 at 19:21
  • @FrançoisAndrieux: Actually it has just enough of this behavior that the memory usage monitor in Windows reflects it. The memory is reserved out of the total but not yet allocated (this bit or operations engineer because of GB of CoW pages that weren't going to get written to still have to have enough swap to back them if they actually get copied, but memory usage doesn't show it). – Joshua Oct 20 '20 at 19:47

2 Answers2

3

Calling new[] without delete[] will indeed leak memory. You might not see it unless you are leaking lots of buffers, depending on how your app's RTL allocates/caches memory under the hood.

Traditionally, using a std::vector<char>, or even a std::string, is the preferred choice for dynamic buffers, let them handle their own memory for you, eg:

std::vector<char> buf(sizeof(obj));
or
std::string buf(sizeof(obj), 0);

If you absolutely need new[]/delete[] for some reason, consider using std::unique_ptr<char[]> instead, let it call delete[] for you, eg:

std::unique_ptr<char[]> buf(new char[sizeof(obj)]);
or
auto buf = std::make_unique<char[]>(sizeof(obj));
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
2

Yep, that looks like a memory leak.

If you don't want to explicitly call delete on dynamically allocated memory, the standard solution is to use a smart pointer, which generally uses "reference counting" in order to de-allocate the memory safely.

More info on smart pointers: What is a smart pointer and when should I use one?

As for why you didn't allocate more memory -- what are you measuring? There are generally two "allocations" that happen:

  1. Your memory allocator requests memory from the operating system.
  2. Your memory allocator returns a pointer to some of that memory.

(Strictly speaking, this is not specific to your compiler, this is based on the implementation of new. If you're using the standard Visual C++ toolchain, then in that sense, yeah, the implementation of new depends on Visual C++. Here's a starting point: https://learn.microsoft.com/en-us/cpp/standard-library/memory?view=vs-2019)

It's possible that you're measuring #1 -- how much memory is being allocated by the operating system. For instance, sizeof(obj) may be 1 kilobyte, and your allocator may have requested 256K from the operating system. So you have space to perform new and receive allocations within that memory buffer, without changing the OS-level memory footprint of your process.

As an experiment, I'd suggest allocating successively larger chunks of memory:

const int alloc_size = 4; // then 5, 6, 7...
for (int i = 0; i < (2<<alloc_size); i++) {
  // do your allocation here
}

and then finding out how many allocations you can perform before your measurement changes.

ajm
  • 144
  • 7