Memory leak due to destructors not running

Hello,

We have integrated the Visual Leak Detector into our project and it has detected memory leaks that appear to be juce related.  I have spent a bit of time looking into them, and it looks like the cause might be that inside the juce_dll, destructors for static and global variable sare not called.

I have done the following experiments.  I created a simple struct that has a constructor and destructor and create an object of this type.  I then put breakpoints in the constructor and destructor and see when they are called.

struct jucetest
{
    int        blah;
    jucetest()
    {
        blah = 2;
    }
    ~jucetest()
    {
        blah = 0;
    }
};
jucetest foobar;

If I put this at the bottom of say juce_win32_Threads.cpp, I will see the constructor run at program startup, but the destructor does not run at program shutdown

If I put an analogous bit of code in the bottom of our main.cpp file, I will see that the constructor is called at program startup and the destructor is called at program shutdown.

I believe that this might be related to how DLLs are loaded an unloaded, but I am not sure.  I recall reading about how destructors may not be called if abort() is called as opposed to exit() and I think there might be analogous issues for DLLs.  These issues might not be that serious as I think they only happen on program shutdown, but they may mask other memory leaks we get in the future.

Any insight would be appreciated.

Thanks,
John Lawrie

 

Here are a few of the memory leaks detected by the Visaul Leak Detector


---------- Block 6466 at 0x0000000002B3FAB0: 24 bytes ----------
Leak Hash: 0x8BA44B78 Count: 1
  Call Stack:
    c:\code\zyamusic\thirdparty\juce\modules\juce_core\threads\juce_thread.cpp (84): juce64_debug.dll!juce::getCurrentThreadHolder + 0xA bytes
    c:\code\zyamusic\thirdparty\juce\modules\juce_core\threads\juce_thread.cpp (91): juce64_debug.dll!juce::Thread::threadEntryPoint + 0xA bytes
    c:\code\zyamusic\thirdparty\juce\modules\juce_core\threads\juce_thread.cpp (119): juce64_debug.dll!juce::juce_threadEntryPoint
    c:\code\zyamusic\thirdparty\juce\modules\juce_core\native\juce_win32_threads.cpp (105): juce64_debug.dll!juce::threadEntryProc
    f:\dd\vctools\crt_bld\self_64_amd64\crt\src\threadex.c (348): MSVCR90D.dll!_callthreadstartex + 0x17 bytes
    f:\dd\vctools\crt_bld\self_64_amd64\crt\src\threadex.c (331): MSVCR90D.dll!_threadstartex
    0x000000007761652D (File and line number not available): kernel32.dll!BaseThreadInitThunk + 0xD bytes
    0x000000007774C541 (File and line number not available): ntdll.dll!RtlUserThreadStart + 0x21 bytes
  Data:
    E8 25 5A E9    FE 07 00 00    01 00 00 00    CD CD CD CD     .%Z..... ........
    CD CD CD CD    CD CD CD CD                                   ........ ........

 

---------- Block 6760 at 0x0000000002B478E0: 39 bytes ----------
Leak Hash: 0x0547061B Count: 1
  Call Stack:
    c:\code\zyamusic\thirdparty\juce\modules\juce_core\text\juce_string.cpp (77): juce64_debug.dll!juce::StringHolder::createUninitialisedBytes + 0xE bytes
    c:\code\zyamusic\thirdparty\juce\modules\juce_core\text\juce_string.cpp (96): juce64_debug.dll!juce::StringHolder::createFromCharPointer<juce::CharPointer_ASCII>
    c:\code\zyamusic\thirdparty\juce\modules\juce_core\text\juce_string.cpp (311): juce64_debug.dll!juce::String::String
    c:\code\zyamusic\thirdparty\juce\modules\juce_graphics\fonts\juce_font.cpp (322): juce64_debug.dll!juce::Font::getDefaultSansSerifFontName + 0x2F bytes
    c:\code\zyamusic\thirdparty\juce\modules\juce_graphics\fonts\juce_font.cpp (191): juce64_debug.dll!juce::Font::SharedFontInternal::SharedFontInternal + 0x59 bytes
    c:\code\zyamusic\thirdparty\juce\modules\juce_graphics\fonts\juce_font.cpp (260): juce64_debug.dll!juce::Font::Font + 0x61 bytes
    c:\code\zyamusic\thirdparty\juce\modules\juce_gui_basics\widgets\juce_texteditor.cpp (948): juce64_debug.dll!juce::TextEditor::TextEditor + 0x339 bytes
    c:\code\zyamusic\soundbetter\soundbettermixer\src\maincomponent.cpp (39): SoundBetterMixer64_debug.exe!MainComponent::MainComponent + 0x4E bytes
    c:\code\zyamusic\soundbetter\soundbettermixer\src\appclass.cpp (62): SoundBetterMixer64_debug.exe!AppClass::initialise + 0x3A bytes
    c:\code\zyamusic\thirdparty\juce\modules\juce_events\messages\juce_applicationbase.cpp (256): juce64_debug.dll!juce::JUCEApplicationBase::initialiseApp + 0x34 bytes
    c:\code\zyamusic\thirdparty\juce\modules\juce_gui_basics\application\juce_application.cpp (89): juce64_debug.dll!juce::JUCEApplication::initialiseApp + 0xA bytes
    c:\code\zyamusic\thirdparty\juce\modules\juce_events\messages\juce_applicationbase.cpp (229): juce64_debug.dll!juce::JUCEApplicationBase::main + 0x1F bytes
    c:\code\zyamusic\soundbetter\soundbettermixer\src\main.cpp (16): SoundBetterMixer64_debug.exe!WinMain + 0x49 bytes
    f:\dd\vctools\crt_bld\self_64_amd64\crt\src\crtexe.c (578): SoundBetterMixer64_debug.exe!__tmainCRTStartup + 0x42 bytes
    f:\dd\vctools\crt_bld\self_64_amd64\crt\src\crtexe.c (403): SoundBetterMixer64_debug.exe!WinMainCRTStartup
    0x000000007761652D (File and line number not available): kernel32.dll!BaseThreadInitThunk + 0xD bytes
    0x000000007774C541 (File and line number not available): ntdll.dll!RtlUserThreadStart + 0x21 bytes
  Data:
    00 00 00 00    CD CD CD CD    10 00 00 00    00 00 00 00     ........ ........
    3C 53 61 6E    73 2D 53 65    72 69 66 3E    00 CD CD CD     <Sans-Se rif>....
    CD CD CD CD    CD CD CD                                      ........ ........


---------- Block 6820 at 0x0000000002BF0A40: 39 bytes ----------
Leak Hash: 0x36C6F5A4 Count: 1
  Call Stack:
    c:\code\zyamusic\thirdparty\juce\modules\juce_core\text\juce_string.cpp (77): juce64_debug.dll!juce::StringHolder::createUninitialisedBytes + 0xE bytes
    c:\code\zyamusic\thirdparty\juce\modules\juce_core\text\juce_string.cpp (96): juce64_debug.dll!juce::StringHolder::createFromCharPointer<juce::CharPointer_ASCII>
    c:\code\zyamusic\thirdparty\juce\modules\juce_core\text\juce_string.cpp (311): juce64_debug.dll!juce::String::String
    c:\code\zyamusic\thirdparty\juce\modules\juce_core\text\juce_stringpool.cpp (64): juce64_debug.dll!juce::StringPoolHelpers::getPooledStringFromArray<char const * __ptr64> + 0x12 bytes
    c:\code\zyamusic\thirdparty\juce\modules\juce_core\text\juce_stringpool.cpp (94): juce64_debug.dll!juce::StringPool::getPooledString + 0x1D bytes
    c:\code\zyamusic\thirdparty\juce\modules\juce_core\text\juce_identifier.cpp (61): juce64_debug.dll!juce::Identifier::Identifier
    c:\code\zyamusic\thirdparty\juce\modules\juce_gui_basics\components\juce_component.cpp (267): juce64_debug.dll!juce::Component::ComponentHelpers::getColourPropertyId + 0x12 bytes
    c:\code\zyamusic\thirdparty\juce\modules\juce_gui_basics\components\juce_component.cpp (2170): juce64_debug.dll!juce::Component::setColour + 0x32 bytes
    c:\code\zyamusic\thirdparty\juce\modules\juce_gui_basics\widgets\juce_label.cpp (39): juce64_debug.dll!juce::Label::Label
    c:\code\zyamusic\thirdparty\juce\modules\juce_gui_basics\lookandfeel\juce_lookandfeel_v2.cpp (1149): juce64_debug.dll!juce::LookAndFeel_V2::createComboBoxTextBox + 0x2F bytes
    c:\code\zyamusic\thirdparty\juce\modules\juce_gui_basics\widgets\juce_combobox.cpp (416): juce64_debug.dll!juce::ComboBox::lookAndFeelChanged + 0x37 bytes
    c:\code\zyamusic\thirdparty\juce\modules\juce_gui_basics\widgets\juce_combobox.cpp (51): juce64_debug.dll!juce::ComboBox::ComboBox
    c:\code\zyamusic\soundbetter\soundbettermixer\src\maincomponent.cpp (48): SoundBetterMixer64_debug.exe!MainComponent::MainComponent + 0x4F bytes
    c:\code\zyamusic\soundbetter\soundbettermixer\src\appclass.cpp (62): SoundBetterMixer64_debug.exe!AppClass::initialise + 0x3A bytes
    c:\code\zyamusic\thirdparty\juce\modules\juce_events\messages\juce_applicationbase.cpp (256): juce64_debug.dll!juce::JUCEApplicationBase::initialiseApp + 0x34 bytes
    c:\code\zyamusic\thirdparty\juce\modules\juce_gui_basics\application\juce_application.cpp (89): juce64_debug.dll!juce::JUCEApplication::initialiseApp + 0xA bytes
    c:\code\zyamusic\thirdparty\juce\modules\juce_events\messages\juce_applicationbase.cpp (229): juce64_debug.dll!juce::JUCEApplicationBase::main + 0x1F bytes
    c:\code\zyamusic\soundbetter\soundbettermixer\src\main.cpp (16): SoundBetterMixer64_debug.exe!WinMain + 0x49 bytes
    f:\dd\vctools\crt_bld\self_64_amd64\crt\src\crtexe.c (578): SoundBetterMixer64_debug.exe!__tmainCRTStartup + 0x42 bytes
    f:\dd\vctools\crt_bld\self_64_amd64\crt\src\crtexe.c (403): SoundBetterMixer64_debug.exe!WinMainCRTStartup
    0x000000007761652D (File and line number not available): kernel32.dll!BaseThreadInitThunk + 0xD bytes
    0x000000007774C541 (File and line number not available): ntdll.dll!RtlUserThreadStart + 0x21 bytes
  Data:
    00 00 00 00    CD CD CD CD    10 00 00 00    00 00 00 00     ........ ........
    6A 63 63 6C    72 5F 31 30    30 30 32 30    31 00 CD CD     jcclr_10 00201...
    CD CD CD CD    CD CD CD                                      ........ ........
 

 

Static destructors will get executed when a DLL is unloaded, so either the DLL isn't getting unloaded properly by whatever host app you're using, or it could just be that the leak detection check is running before the DLL is unloaded.

Either way, it's not a big deal, worst case is that a handful of bytes gets leaked once per run.

And my advice has always been to avoid DLLs. This is just yet another reason why they're a PITA.

I got this a lot, then realised what the issue is. You need to use

virtual ~jucetest()

otherwise your destructor won't call the base class destructor, and hence the memory leaks. (Thinks: I have got this right how C++ works right?!? I spend too much time in C# land).

I had a lot of abstract classes without virtual destructors and this one bit me hard.