My hobby project Med is written in C++. A lot of implementations need to use dynamic memory allocation and instantiation. Because complex data is impractical to be passed by value, like the case of JavaScript object and array. Since C++ doesn’t have garbage collection, it is possible that the developer doesn’t free/delete the dynamically created memory properly.
As in my project Med, the program will retrieve the memory from other process. That means, it needs to make a copy of the scanned memory. And this will involve creating dynamic memory (using new
operator). When using the program to filter the memory to get the target result, it needs to get a new copy of memory with the updated values, then compare with the previous copy. The unmatched will need to be discarded (free/delete); the matched will need to replace the old one (also free/delete the old one, because the new one is dynamically allocated).
Though it can be easily expressed in verbal form, writing the algorithm is usually inducing some human mistakes, such as using wrong variable name. As a result, we may access the memory that is not dynamically allocated, or freeing the memory twice. These will cause segmentation fault, and C++ compiler will not tell you where the error comes from. Debugging is hellish in this case. As a result, I used several solutions to fix memory leaking.
Adopt TDD
I have covered this in my previous post. Smaller function is easier to be tested. Just make sure your functions or methods are testable.
Valgrind (Linux only)
Compile your application with debugging information (g++
with -g
option). Then run your test suite with valgrind
.
valgrind --leak-check=yes ./testSnapshot
It will tell you which function access the non-allocated memory, how many bytes are untracked, and so on.
Valgrind is super useful to check memory leaking.
Memory Manager
Because I wrote the code with some mistakes, so the memory is freed twice and causes the error. But I failed to find where is the cause (which finally I found it).
Therefore, I wrote a memory manager to make sure the memory will not be freed more than once. However, this is actually not a good solution to avoid memory leaking. It just prevents the program to free the memory twice.
ByteManager::ByteManager() {} ByteManager::~ByteManager() { clear(); } // Singleton ByteManager& ByteManager::getInstance() { static ByteManager instance; return instance; } Byte* ByteManager::newByte(int size) { Byte* byte = new Byte[size]; recordedBytes[byte] = byte; return byte; } void ByteManager::deleteByte(Byte* byte) { auto found = recordedBytes.find(byte); if (found != recordedBytes.end()) { delete[] recordedBytes[byte]; recordedBytes.erase(found); } } void ByteManager::clear() { for (auto it = recordedBytes.begin(); it != recordedBytes.end(); ++it) { delete[] it->second; } recordedBytes.clear(); } map ByteManager::getRecordedBytes() { return recordedBytes; }
Then, I refactored both new
operator and delete
operator to be replaced by newByte() and deleteByte() methods. And I use a std::map
to store all the created memory.
Smart pointer
C++11 introduced smart pointers shared_ptr
and unique_ptr
. unique_ptr
can be replaced by auto_ptr
before C++11. By using smart pointer, we can use new operator to instantiate an object, then we need not to delete it. Because it will be deleted automatically when it lost the reference. For example,
typedef std::shared_ptr<SnapshotScan> SnapshotScanPtr; // some where else SnapshotScanPtr matched = SnapshotScanPtr(new SnapshotScan(curr.getAddress() + i + currOffset, scanType));
So, program will instantiate a new SnapshotScan
object as the shared_ptr
. Then it will be stored in a std::vector
. When it lost the reference, such as removed from the std::vector
, it will be deleted automatically.
In my opinion, it is a better solution than the Memory Manager above. However, my existing project can be hardly refactored to use smart pointer.