I’m not sure whether that such test can realiably diagnose a leak. I see a loop that creates 400 objects and Python’s garbage colletion and memory management are a black box. I believe you can try forcing a GC after running the loop and then measure memory consumption.
I followed your advice, and simplify the loop. I still observe a leak. The RAM is slowly increasing. See below the code:
def test_memory_leak():
for time in range(200):
gc.collect()
ren = vtkRenderer()
renWin = vtkRenderWindow()
renWin.AddRenderer(ren)
renWin.SetWindowName('Screenshot')
gc.collect()
test_memory_leak()
It’s been a very long time, I did not need to touch C++. I will try to find the time to reproduce this example and see if I observe this leak in C++ also.
I have just encountered this same issue in c++. Version 9.2.x, MacOS, Intel silicon. The render window is not closing, possibly having to do with using vtkPNGWriter and no window interactor. When run in lldb, after 100 iterations a warning pops up saying “[Window] WARNING: NSWindow has detected an excessive live window count of 101. Window 0x1e69 of class ‘NSWindow’ created after passing the threshold of 100.” I’ll post the backtrace at the bottom here.
I encountered this at first creating the renderWindow inside a function that is looped, then tried a workaround where a single renderWindow is passed into the function inside the loop. In every case, Finalize() is supposed to free the resources, but does not. More troubling is the fact that even the object going out of scope (when inside the function it was wrapped in a vtkNew) does not free the resources.
As a side note, I tried to run VTK_DEBUG_LEAKS but couldn’t get it to report anything. If anyone knows a good tutorial that would be really helpful.
[Window] WARNING: NSWindow has detected an excessive live window count of 101. Window 0x1e69 of class 'NSWindow' created after passing the threshold of 100. This window is not necessarily the cause, and this warning will only be shown once per window class. (
0 AppKit 0x00007ff811a41b35 -[NSWindow _setWindowNumber:] + 837
1 AppKit 0x00007ff8123cc4ef _NXCreateWindow + 278
2 AppKit 0x00007ff811b4d71e -[NSWindow _commonAwake] + 871
3 AppKit 0x00007ff811a40b5f -[NSWindow _commonInitFrame:styleMask:backing:defer:] + 1209
4 AppKit 0x00007ff811a40290 -[NSWindow _initContent:styleMask:backing:defer:contentView:] + 1047
5 AppKit 0x00007ff811a3fe72 -[NSWindow initWithContentRect:styleMask:backing:defer:] + 42
6 libvtkRenderingOpenGL2-9.2.9.2.6.dy 0x0000000103c60d4d _ZN20vtkCocoaRenderWindow13CreateAWindowEv + 1859
7 libvtkRenderingOpenGL2-9.2.9.2.6.dy 0x0000000103cd1c61 _ZN21vtkOpenGLRenderWindow5StartEv + 31
8 libvtkRenderingOpenGL2-9.2.9.2.6.dy 0x0000000103c6137b _ZN20vtkCocoaRenderWindow5StartEv + 15
9 libvtkRenderingCore-9.2.9.2.6.dylib 0x000000010c049f5d _ZN15vtkRenderWindow6RenderEv + 169
10 libvtkRenderingOpenGL2-9.2.9.2.6.dy 0x0000000103cd3d27 _ZN21vtkOpenGLRenderWindow6RenderEv + 73
11 genVTKimages 0x000000010005e33f _ZN8swiftLib14testVTKDataVizE15vtkSmartPointerI15vtkRenderWindowERNSt3__16vectorIS0_I11vtkPolyDataENS3_9allocatorIS6_EEEESA_PNS4_IdNS7_IdEEEEmmPNS3_12basic_stringIcNS3_11char_traitsIcEENS7_IcEEEE + 2191
12 genVTKimages 0x000000010005f115 _ZN8swiftLib14testVTKDataVizE15vtkSmartPointerI15vtkRenderWindowES0_I11vtkPolyDataES4_PNSt3__16vectorIdNS5_9allocatorIdEEEEmmPNS5_12basic_stringIcNS5_11char_traitsIcEENS7_IcEEEE + 437
13 genVTKimages 0x000000010005d751 _ZN8swiftLib14testVTKDataVizE15vtkSmartPointerI15vtkRenderWindowES0_I11vtkPolyDataES0_I9vtkPointsEPNSt3__16vectorIdNS7_9allocatorIdEEEEmmPNS7_12basic_stringIcNS7_11char_traitsIcEENS9_IcEEEE + 209
14 genVTKimages 0x000000010005d5ff _ZN8swiftLib17dataVizComponents11writeOutImgENSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEE15vtkSmartPointerI15vtkRenderWindowE + 143
15 genVTKimages 0x000000010005d992 _ZN8swiftLib17dataVizComponents11writeOutImgE15vtkSmartPointerI15vtkRenderWindowE + 98
16 genVTKimages 0x0000000100041e15 main + 7237
17 dyld 0x00007ff80e5fc41f start + 190
Here it is, as an MRE. Note that the problem persists even if, as here, the vtkRenderWindow is rendering a blank scene. The preprocessor directives are to quickly verify the issue exists whether one vtkRenderWindow is instantiated for the entire loop and passed in, or within the looped function.
#include <sstream>
#include <vtkPNGWriter.h>
#include <vtkRenderWindow.h>
#include <vtkSmartPointer.h>
#include <vtkWindowToImageFilter.h>
// only turn one of these on at a time
//#define ONE_RENDERER // instantiate vtkRenderWindow outside the loop and pass into fcn
#define MANY_RENDERERS // instantiate vtkRenderWindow inside fcn
void testVTKDataViz(
#ifdef ONE_RENDERER
vtkSmartPointer<vtkRenderWindow> renWin,
#endif
std::string OPpath) {
#ifdef MANY_RENDERERS
vtkNew<vtkRenderWindow> renWin;
renWin->OffScreenRenderingOn();
#endif
renWin->Render();
vtkNew<vtkWindowToImageFilter> imgf;
imgf->SetInput(renWin);
imgf->SetInputBufferTypeToRGB();
imgf->Update();
vtkNew<vtkPNGWriter> wr;
wr->SetFileName(OPpath.c_str());
wr->SetInputConnection(imgf->GetOutputPort());
wr->Write();
#ifdef MANY_RENDERERS
renWin->Finalize();
#endif
}
int main() {
#ifdef ONE_RENDERER
auto renWin = vtkSmartPointer<vtkRenderWindow>::New();
renWin->OffScreenRenderingOn();
#endif
std::string test_dir = "./imgDump";
for (size_t ii=0; ii<1500; ii++) {
std::stringstream ssPath;
ssPath << test_dir << "/sphere_" << ii << ".png";
std::cout << ssPath.str() << std::endl;
#ifdef MANY_RENDERERS
testVTKDataViz(ssPath.str());
#endif
#ifdef ONE_RENDERER
testVTKDataViz(renWin, ssPath.str());
renWin->Finalize();
#endif
}
return EXIT_SUCCESS;
}
If you look into the code of vtkWindowToImageFilter’s SetInput() method:
void vtkWindowToImageFilter::SetInput(vtkWindow* input)
{
if (input != this->Input)
{
if (this->Input)
{
this->Input->UnRegister(this);
}
this->Input = input;
if (this->Input)
{
this->Input->Register(this);
}
this->Modified();
}
}
Notice the vtkObjectBase::Register() method which, in the end, prevents the instance from being deallocated once its smart pointer goes out of the scope, hence the “memory leak”. You need to call imgf->SetInput(nullptr) to un-register the instance with the renWin object before leaving the scope of testVTKDataViz(). The way your test is coded, getting that VTK warning is expected.
Thanks for the suggestion. I tried it and am still having the same issue. Sequential calls to imgf->GetInput() before and after imgf->SetInput(nullptr) result in the following:
0x7faaebc04080
0x0
This seemingly confirms the vtkRenderWindow has been released by the vtkWindowToImageFilter. However, the memory problem remains. For good measure I tried also to SetInputConnection(nullptr) on the vtkPNGWriter, but no effect.