I am reading Effective Modern C++ (Scott Meyers) and trying out something from item 21. The book says a side effect of using std::make_shared is that memory cannot be freed until all shared_ptrs and weak_ptrs are gone (because the control block is allocated together with the memory).
I expected that this would mean that if I keep a cache around holding a bunch of weak_ptrs that no memory would ever be freed. I tried this using the code below, but as the shared_ptrs are removed from the vector, I can see using pmap that memory is actually being freed. Can anyone explain me where I am going wrong? Or if my understanding is wrong?
Note: the function loadWidget is not the same as in the book for the purpose of this experiment.
#include <iostream>
#include <memory>
#include <unordered_map>
#include <vector>
#include <thread>
#include <chrono>
class Widget {
public:
Widget()
: values(1024*1024, 3.14)
{ }
std::vector<double> values;
};
std::shared_ptr<Widget> loadWidget(unsigned id) {
return std::make_shared<Widget>();
}
std::unordered_map<unsigned, std::weak_ptr<Widget>> cache;
std::shared_ptr<Widget> fastLoadWidget(unsigned id) {
auto objPtr = cache[id].lock();
if (!objPtr) {
objPtr = loadWidget(id);
cache[id] = objPtr;
}
return objPtr;
}
int main() {
std::vector<std::shared_ptr<Widget>> widgets;
for (unsigned i=0; i < 20; i++) {
std::cout << "Adding widget " << i << std::endl;
widgets.push_back(fastLoadWidget(i));
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
while (!widgets.empty()) {
widgets.pop_back();
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
return 0;
}
It is true that when you use std::make_shared the storage for the new object and for the control block is allocated as a single block, so it is not released as long as there exists a std::weak_ptr to it. But, when the last std::shared_ptr is destroyed the object is nonetheless destroyed (its destructor runs and its members are destroyed). It's just the associated storage which remains allocated and unoccupied.
std::vector allocates storage dynamically for its elements. This storage is external to the std::vector, it is not part of the object's memory representation. When you destroy a Widget you also destroy its std::vector member. That member's destructor will release the dynamically allocated memory used to store its elements. The only memory that can't be release immediately is the control block and the storage for Widget (which should be sizeof(Widget) bytes). It does not prevent the storage for the elements of the vector from being released immediately.
Related
I have a program that allocates pointers on a stack.
#include <stack>
class A{};
int main()
{
std::stack<A*> pAs;
for (int i = 0; i < 100; i++)
{
A* pA = new A{};
pAs.push(pA);
}
}
The following is what I was doing.
while (!pAs.empty())
{
delete pAs.top();
pAs.pop();
}
My logic in doing this is that delete pAs.top() deletes the pointer and I have to remove the remaining pointer that points to an "invalid memory location" using pAs.pop() myself. I tried to test if the size decreases with every deletion
while (!pAs.empty())
{
delete pAs.top();
std::cout << pAs.size() << '\n'
}
but I got some error saying some file not loaded.
Or maybe this?
while (!pAs.empty())
{
pAs.pop(); //Does .pop() automatically deallocate memory?
}
What should I do?
How should I deallocate pointers allocated using new
By storing them in a smart pointer upon creation, and destroying that smart pointer when you desire to deallocate.
stored on std::stack
Example:
{
std::stack<std::unique_ptr<A>> pAs;
pAs.push(std::make_unique<A>());
} // the dynamic object is correctly deallocated here
//Does .pop() automatically deallocate memory?
std::stack::pop only erases the element from the stack. It doesn't do anything else, for example, it won't call delete on the element. If the element is a bare pointer with sole ownership of dynamic memory, then that memory will leak.
P.S. Avoid unnecessary dynamic allocation. Consider whether std::stack<A> would be a more efficient choice.
I have a std::vector<std::unique_ptr<Kind>> which I want to clean up while it is being iterated upon, without explicitly calling the destructor of its members (.reset()).
The Kind is a heavy struct and its size increases during the iteration. The next object doesn't need to know about previous objects so I'd like to clean up an iterand when its not needed.
I know vector will clean up in the end, but by then, lots of Kind and their dynamically allocated memory adds up. I'm trying to reduce peak memory to just one element.
I want to avoid reset since other developers may not know about the dynamic allocation, forget calling reset in the end of the loop and cost memory penalty.
I cannot create a copy,
for(std::unique_ptr<Kind> t : store)
I cannot move it like
for(std::unique_ptr<Kind> &&t : store)
Then how do I do it ?
#include <iostream>
#include <vector>
struct Kind{
char a;
char *array;
Kind(const char c): a(c)
{
}
~Kind(){
free(array); // internal custom deallocator.
}
};
int main() {
std::vector<std::unique_ptr<Kind>> store;
store.push_back(std::make_unique<Kind>('y'));
store.push_back(std::make_unique<Kind>('z'));
for(std::unique_ptr<Kind> &t : store){
// increase size of Kind.array.
std::cout << t->a;
// Use the Kind.array
// clean up t automatically.
}
return 0;
}
Example of moving the element out of the vector.
int main() {
std::vector<std::unique_ptr<Kind>> store;
store.push_back(std::make_unique<Kind>('y'));
for(std::unique_ptr<Kind> &t : store){
auto tmp = std::move(t); // leaving a valid but empty entry in store
std::cout << tmp->a;
// clean up t automatically.
// tmp runs out of scope and cleans up
}
return 0;
}
In effect not much different from the reset, but might be relevant for what you actually do in your real program.
How to take ownership of an object while looping over std::vector of std::unique_ptr using a range based for loop?
Loop with a reference to the element, and std::move the unique pointer into another. Example:
for(std::unique_ptr<Kind> &t : store){
std::unique_ptr<Kind> owner = std::move(t);
// do something with newly owned pointer
I want to clean up
there's no need to keep older structs around
You could deallocate the object by resetting the pointer:
for(std::unique_ptr<Kind> &t : store) {
// do something
t.reset();
That said, this is typically unnecessary. They will be automatically be destroyed when the vector goes out of scope.
I'm trying to save some memory here
If you allocate dynamic objects while iterating this may be useful. Otherwise it won't affect peak memory use.
If you want to make sure the instances are deleted immediately after each iteration and you cannot wait until the entire loop is done, you can write a wrapper that takes care of that and expresses your intent at the same time:
template <typename T>
struct Stealing {
std::unique_ptr<T> ptr;
Stealing(std::unique_ptr<T>& ptr) : ptr(std::move(ptr)) {
}
auto operator*() {
return ptr.operator*();
}
auto operator->() {
return ptr.operator->();
}
}
You can use that in the loop as a drop-in replacement for a unique_ptr as such:
for (Stealing<Kind> t: store) {
// do what you like with t as if it was a std::unique_ptr
// when t goes out of scope, so does its member -> Kind gets destroyed
}
I am working on an open-source library that has a memory leak in it. The library is a data streaming service built around boost::asio. The server side uses heap memory management system which provides memory to hold a finite number of samples while they wait to get pushed accross a tcp connection. When the server is first constructed, a heap of memory for all the old samples is allocated. From this heap, after a sample is passed accross the socket, the memory is returned to the heap.
This is fine, unless all that pre-allocated heap is already taken. Here is the function that creates a 'sample':
sample_p new_sample(double timestamp, bool pushthrough) {
sample *result = pop_freelist();
if (!result){
result = new(new char[sample_size_]) sample(fmt_, num_chans_, this);
}
return sample_p(result);
}
sample_p is just a typedef'd smart pointer templated to the sample class.
The offending line is in the middle. When there isn't a chunk of memory on the freelist, we need to make some. This leaks memory.
My question is why is this happening? Since I shove the new sample into a smart pointer, shouldn't the memory be freed when it goes out of scope (it gets popped off of a stack later on.)? Do I need to somehow handle the memory allocated on the inside---i.e. the memory allocated by new char[sample_size_]? If yes, how can I do that?
Edit:
#RichardHodges here is a compile-able MCVE. This is highly simplified but I think it captures exactly the problem I am facing in the original code.
#include <boost/intrusive_ptr.hpp>
#include <boost/lockfree/spsc_queue.hpp>
#include <iostream>
typedef boost::intrusive_ptr<class sample> sample_p;
typedef boost::lockfree::spsc_queue<sample_p> buffer;
class sample {
public:
double data;
class factory{
public:
friend class sample;
sample_p new_sample(int size, double data) {
sample* result = new(new char[size]) sample(data);
return sample_p(result);
}
};
sample(double d) {
data = d;
}
void operator delete(void *x) {
delete[](char*)x;
}
/// Increment ref count.
friend void intrusive_ptr_add_ref(sample *s) {
}
/// Decrement ref count and reclaim if unreferenced.
friend void intrusive_ptr_release(sample *s) {
}
};
void push_sample(buffer &buff, const sample_p &samp) {
while (!buff.push(samp)) {
sample_p dummy;
buff.pop(dummy);
}
}
int main(void){
buffer buff(1);
sample::factory factory_;
for (int i = 0; i < 10; i++)
push_sample(buff, factory_.new_sample(100,0.0));
std::cout << "press any key to exit" << std::endl;
char foo;
std::cin >> foo;
return 0;
}
When I step through the code, I note that my delete operator never gets called on the sample pointers. I guess that the library I'm working on (which again, I didn't write, so I am still learning its ways) is mis-using the intrusive_ptr type.
You are allocating the memory with new[] so you need to deallocate it with delete[] (on a char*). The smart pointer probably calls delete by default, so you should provide a custom deleter that calls delete[] (after manually invoking the destructor of the sample). Here is an example using std::shared_ptr.
auto s = std::shared_ptr<sample>(
new (new char[sizeof(sample)]) sample,
[](sample* p) {
p->~sample();
delete[] reinterpret_cast<char*>(p);
}
);
However, why you are using placement new when your buffer only contains one object? Why not just use regular new instead?
auto s = std::shared_ptr<sample>(new sample);
Or even better (with std::shared_ptr), use a factory function.
auto s = std::make_shared<sample>();
Consider the following contrived example:
class AllocatedClass {
public:
AllocatedClass() : dummy(0) {}
private:
int dummy;
};
class AllocatingClass {
public:
AllocatingClass() : list() {}
~AllocatingClass() {
// CANNOT delete the elements in list here because there may
// be more than on instance of AllocatingClass sharing the same
// static list
}
AddNewObject() {
list.push_back(new AllocatedClass());
}
private:
static std::vector<AllocatedClass*> list;
};
In implemetation file
std::vector<AllocatedClass*> AllocatingClass::list;
Putting aside whether multiple instances of a class should share a list of dynamically allocated objects is a good idea, is there a way clean up these new'ed AllocatedClass objects at the end of the program? Does it matter if these never get deleted considering I don't want to delete them until application end?
If the lifetime of the object is the lifetime of the execution of the program then there is no need to free the memory with code. The memory will be free automatically.
Many Linux command line tools do not free their memory in code for performance reasons. (Faster to automatically free pages of memory, than to free each object one by one.)
Another strategy is to keep a separate list of unique AllocatedClass instances, then free them from that list later on (switching ownership of the objects). Like std::list<AllocatedClass*> to_be_freed.
is there a way clean up these new'ed AllocatedClass objects at the end
of the program?
One solution is to use std::shared_ptr and have the deallocation done automatically.
#include <memory>
#include <vector>
#include <iostream>
class AllocatedClass
{
public:
AllocatedClass(int n = 0) : dummy(n) {}
~AllocatedClass() { std::cout << "I am being destroyed" << '\n'; }
private:
int dummy;
};
class AllocatingClass
{
public:
AllocatingClass() {}
void AddNewObject(int num = 0)
{ shared_list.push_back(std::make_shared<AllocatedClass>(num)); }
private:
static std::vector<std::shared_ptr<AllocatedClass>> shared_list;
};
std::vector<std::shared_ptr<AllocatedClass>> AllocatingClass::shared_list;
AllocatingClass ac;
int main()
{
ac.AddNewObject();
ac.AddNewObject(1);
}
Live Example
Note that the destructors are called automatically for the objects that were placed in the vector.
(BTW, it isn't a good idea to name your member variable list).
Most of the time(very close to all the time) an object that creates dynamically allocated objects should have a programmer defined destructor to free the memory when the object reaches the end of its life. AllocatingClass should have a destructor because you allocate dynamic memory with new.
~AllocatingClass() {
for (int i = 0 ; i < list.size();i++) {
if(list[i] != NULL) {
delete list[i];
list[i] = NULL;
}
}
This should both provide a method for de-allocating memory and safety so that you do not delete an already deleted pointer.
I'm trying to make this code work, but the object keep getting destroyed...
I've found that it has to do with the object being copied to the vector, but can't find any way to prevent it...
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Obje
{
private:
static int instances;
int id;
public:
static int getInstances();
void getId();
virtual void myClass();
Obje(int auxId);
~Obje();
};
int Obje::instances = 0;
int Obje::getInstances()
{
return instances;
}
Obje::Obje(int auxId)
{
this->id = auxId;
cout << "Obje Created." << endl;
Obje::instances++;
}
Obje::~Obje()
{
cout << "Obje Destroyed." << endl;
Obje::instances--;
}
void Obje::myClass()
{
cout << "Obje." << endl;
}
void Obje::getId()
{
cout << this->id << endl;
}
int main()
{
vector <Obje> list;
Obje *a = new Obje(59565);
list.push_back(*a);
Obje *b = new Obje(15485);
list.push_back(*b);
for(vector<Obje>::iterator it = list.begin(); it != list.end(); ++it)
{
it->getId();
}
return 0;
}
It Generates this output:
Obje Created.
Obje Created.
Obje Destroyed.
59565
15485
Obje Destroyed.
Obje Destroyed.
What does it mean the T(const T& new); i've saw as fix for this?
First of all, it is a bad practice to allocate an object in heap without using smart pointers and forgetting to delete it. Especially, when you are creating it just to make a copy of it.
list.push_back(*a); creates a copy of *a in vector. To create an item in vector without copying another item, you can do list.emplace_back(/*constructor parameters*/);, which is available from c++11. (see http://en.cppreference.com/w/cpp/container/vector/emplace_back)
So, to make the result behavior match your expectations, you should go
vector <Obje> vec;
vec.emplace_back(59565);
vec.emplace_back(15485);
for(const auto & item : vec)
{
item.getId();
}
By the way, it is also a quite bad practice to call a vector as a list, as a list is a different container type and reading such code may be confusing a bit. I guess, I am starting being annoying, but it is better to call method getId as showId as now it returns nothing.
Regarding the use of heap, new and pointer, see my comment in your question.
Regarding the issue object was destroyed, the vector maintains an internal buffer to store object. When you push_back new object to the vector, if its internal buffer is full, it will (the stuff which will be executed when exception occurs won't be mentioned here.):
allocate new internal buffer which is big enough to store its new data.
move data from old internal buffer to new internal buffer.
destroy old buffer.
Hence, your object will be destroyed and copied to new location in this case, hence copy constructor will make it clearer to you.
P/S: AFAIK, some compilers move its data by memmove or std::move