Speicherleck



  • Ich verwende "crt dump memory leaks" und versuche Speicherlecks zu finden.
    Es meldet bei diesem Programm welche:

    #include "ImageManager.h"
    
    ImageManager::ImageManager(std::string rel_path)
    {
    
    	boost::filesystem::path path(boost::filesystem::initial_path().string() + rel_path);
    	boost::filesystem::directory_iterator end;
    
    	if ( boost::filesystem::is_directory(path) ) 
    	{
    		int size = 0;
    		for (boost::filesystem::directory_iterator iter(path); iter != end; ++iter)
    			++size;
    
    		images.reserve(size);
    
    		std::size_t i = 0;
    		for (boost::filesystem::directory_iterator iter(path); iter != end; ++iter, ++i)
    		{
    			sf::Image temp;
    			sf::Sprite sprite;
    			if (temp.LoadFromFile(iter->path().string()))
    			{
    				images.push_back(temp);
    				sprite.SetImage(images[i]);
    				sprites[iter->path().stem().string()] = sprite;
    			}
    		}
    	}
    	else
    	{
    		throw(std::logic_error("File not found"));
    	}
    }
    
    sf::Sprite ImageManager::get_sprite(std::string name)
    {
    	if (sprites.count(name))
    		return sprites[name];
    	else
    		throw(std::logic_error("IMG not found"));
    }
    
    #ifndef IMAGE_MANAGER_H
    #define IMAGE_MANAGER_H
    
    #include <boost\filesystem\operations.hpp>
    #include <boost\filesystem\path.hpp>
    #include <SFML\Graphics.hpp>
    
    #include <vector>
    #include <string>
    #include <map>
    #include <stdexcept>
    #include <cstddef>
    
    class ImageManager
    {
    public:
    	ImageManager(std::string);
    	sf::Sprite get_sprite(std::string name);
    private:
    	std::map<std::string,sf::Sprite> sprites;
    	std::vector<sf::Image> images;
    };
    
    #endif
    
    #define _CRTDBG_MAP_ALLOC
    #include <stdlib.h>
    #include <crtdbg.h>
    #include "ImageManager.h"
    
    int main()
    {
    	ImageManager img("/img");
    	sf::RenderWindow App(sf::VideoMode(800, 600, 32), "Image Management Test");
    	 while (App.IsOpened())
        {
            // Process events
            sf::Event Event;
            while (App.GetEvent(Event))
            {
                // Close window : exit
                if (Event.Type == sf::Event::Closed)
                    App.Close();
            }
    
    		App.Clear();
    		App.Draw(img.get_sprite("Unbenannt"));
    		App.Display();
    
    	 }
    	 _CrtDumpMemoryLeaks();
    }
    

    Ich kann das Leck einfach nicht finden!



  • Visual Studio luegt.



  • Lecker schrieb:

    Ich verwende "crt dump memory leaks" und versuche Speicherlecks zu finden.
    Es meldet bei diesem Programm welche:

    Meldet es auch, an welcher Stelle der Speicher geholt wurde? Gut möglich, dass da noch globale Objekte von den Bilbiotheken rumhängen, die erst nach dem Verlassen von main() gelöscht werden. Ein Profiler wird dir etwas mehr sagen können.



  • Tatsächlich benutzt SFML einige globale Objekte (z.B. OpenGL-Context), welche erst nach dem Prüfen nach Memory Leaks zerstört werden.

    Übrigens würde ich eher std::runtime_error statt std::logic_error verwenden, ausserdem empfehle ich Slashes statt Backslashes in Include-Pfaden.



  • Hier noch ein Memory Leak Detector für Visual Studio:
    http://vld.codeplex.com/

    Ev. kommen da andere Resultate dabei raus.



  • Lecker schrieb:

    #include "ImageManager.h"
    
    ImageManager::ImageManager(std::string rel_path) //const std::string &
    {
    
    	boost::filesystem::path path(boost::filesystem::initial_path().string() + rel_path); //path hat einen operator / für so etwas, der das korrekt macht
    	boost::filesystem::directory_iterator end;
    	
    	if ( boost::filesystem::is_directory(path) ) 
    	{
    		int size = 0; //size_t meinst du wohl
    		for (boost::filesystem::directory_iterator iter(path); iter != end; ++iter)
    			++size;
    
    		images.reserve(size); //wozu? Das zusätzliche Iterieren über das Verzeichnis dauert vermutlich viel länger als das Reallozieren
    
    		std::size_t i = 0; //ist das nötig?
    		for (boost::filesystem::directory_iterator iter(path); iter != end; ++iter, ++i)
    		{
    			sf::Image temp;
    			sf::Sprite sprite;
    			if (temp.LoadFromFile(iter->path().string())) //Fehlerbehandlung?
    			{
    				images.push_back(temp);
    				sprite.SetImage(images[i]); //meinst du vielleicht images.back() ?
    				sprites[iter->path().stem().string()] = sprite;
    			}
    		}
    	}
    	else
    	{
    		throw(std::logic_error("File not found")); //welche Datei?
    	}
    }
    
    sf::Sprite ImageManager::get_sprite(std::string name) //const std::string &
    {
    	if (sprites.count(name))
    		return sprites[name];
    	else
    		throw(std::logic_error("IMG not found"));
    }
    


  • theta schrieb:

    Hier noch ein Memory Leak Detector für Visual Studio:
    http://vld.codeplex.com/

    Ev. kommen da andere Resultate dabei raus.

    Danke für den Tipp.

    @Tyroxx
    Danke für die Tipps, diese Designfehler hab ich übersehen.
    Das mit dem reserve ist deswegen da, weil
    ein sf::Sprite eine Referenz auf ein sf::Image
    hat und bei Reallokation werden Referenzen
    kapputt.

    Ich hab mir schon überlegt std::list
    oder boost::shared_array zu nehmen.



  • Lecker schrieb:

    @Tyroxx
    Danke für die Tipps, diese Designfehler hab ich übersehen.
    Das mit dem reserve ist deswegen da, weil
    ein sf::Sprite eine Referenz auf ein sf::Image
    hat und bei Reallokation werden Referenzen
    kapputt.

    sf::Sprite verwendest du falsch. Das erstellt man, wenn es benötigt wird und dann auch gerne mehrmals pro Bild.
    Außerdem: Wer sagt denn, dass der Iterator beide Male die gleichen Dateien auflistet? Die Anzahl kann sich doch jederzeit ändern.



  • Danke, das war mir nicht bewusst.
    Eine letzte Frage. Ich will die

    sf::Image
    

    s als

    const &
    

    zurückgeben:

    const sf::Image & ImageManager::getImage(const std::string & name)
    {
    	if (images.count(name))
    		return images[name];
    	else
    		throw(std::runtime_error("IMG not found"));
    }
    

    Ist das gutes Design? Ich werd das ganze in eine andere Klasse packen und die Images werden nicht zerstört sein, bevor ich sie benutze.

    PS: Der Visual Leak Detector erkennt keine Leaks.



  • Lecker schrieb:

    Es meldet bei diesem Programm welche:
    (...)

    Guckst du (Kommentar im Code):

    int main()
    {
    	ImageManager img("/img");
    	sf::RenderWindow App(sf::VideoMode(800, 600, 32), "Image Management Test");
    	while (App.IsOpened())
    	{
    		// Process events
    		sf::Event Event;
    		while (App.GetEvent(Event))
    		{
    			// Close window : exit
    			if (Event.Type == sf::Event::Closed)
    				App.Close();
    		}
    
    		App.Clear();
    		App.Draw(img.get_sprite("Unbenannt"));
    		App.Display();
    
    	}
    	// "ImageManager img" und "sf::RenderWindow App" sind hier noch im Scope,
    	// wenn die dynamisch Speicher anfordern (wovon auszugehen ist),
    	// dann ist es ganz normal dass _CrtDumpMemoryLeaks() Leaks melder
     	_CrtDumpMemoryLeaks();
    }
    

    Also besser so

    int main()
    {
    	{
    		ImageManager img("/img");
    		sf::RenderWindow App(sf::VideoMode(800, 600, 32), "Image Management Test");
    
    		while (App.IsOpened())
    			...
    	}
    
     	_CrtDumpMemoryLeaks();
    }
    


  • Nochwas...

    sf::Sprite merkt sich einen Zeiger auf das sf::Image .
    Die Adresse des sf::Image darf sich also nicht ändern.
    Tut sie aber, wenn du die sf::Image Instanzen in einem std::vector verwaltest, und neue Bilder hinzufügst.

    Wenn du die Bilder immer im Konstruktor lädst, und danach keine weiteren mehr hinzufügst, dann ist das OK.
    Wenn du aber Bilder hinzufügst nachdem get_sprite aufgerufen wurde, dann kannst du da ein Problem bekommen.

    ------------

    const sf::Image & ImageManager::getImage(const std::string & name)
    ...
    

    Ist das gutes Design? Ich werd das ganze in eine andere Klasse packen und die Images werden nicht zerstört sein, bevor ich sie benutze.

    Och da kann man vermutlich ganz unterschiedlicher Meinung sein 🙂

    Ich finde es komisch.
    Und zwar weil sf::Image in dieser Anwendung nicht "Wert-Semantik" hat, sondern "Objekt-Semantik" (heisst: die Adresse der sf::Image Dinger ist relevant).
    In so einem Fall würde ich eher nen Zeiger zurückgeben. Referenz finde ich da einfach komisch.



  • hustbaer schrieb:

    const sf::Image & ImageManager::getImage(const std::string & name)
    ...
    

    Ist das gutes Design? Ich werd das ganze in eine andere Klasse packen und die Images werden nicht zerstört sein, bevor ich sie benutze.

    Och da kann man vermutlich ganz unterschiedlicher Meinung sein 🙂

    Ich finde es komisch.
    Und zwar weil sf::Image in dieser Anwendung nicht "Wert-Semantik" hat, sondern "Objekt-Semantik" (heisst: die Adresse der sf::Image Dinger ist relevant).
    In so einem Fall würde ich eher nen Zeiger zurückgeben. Referenz finde ich da einfach komisch.

    Toll: Schnittstelle aufgeweicht, nur um einen seltenen Fehler zu erschweren.
    Wer mit der Referenz das Objekt kopiert, bekommt dieses Kunststück auch mit einem Zeiger hin.
    Und die Verwirrung, weil auf einmal manche Zeiger Null sein können und andere nicht, gibts gratis dazu.

    Referenz ist da völlig in Ordnung. Wenn eine Funktion eine Referenz zurückgibt, ist schon klar, dass da keine Wertsemantik vorliegt. Kann aber noch einmal dabeistehen.

    Nochmal 👍 für die Verwendung von Boost, std , const und Referenzen. Das ist selten bei Fragenden hier.



  • theta schrieb:

    Hier noch ein Memory Leak Detector für Visual Studio:
    http://vld.codeplex.com/

    Ev. kommen da andere Resultate dabei raus.

    Then it can be used with any C/C++ project simply by adding the following line to your code:
    #include <vld.h>

    Ist das egal, wo man vld.h inkludiert? Geht das auch wenn das Programm aus mehreren Projekten/DLLs besteht oder muss man in jedem Projekt vld.h inkludieren?



  • nefrage schrieb:

    Ist das egal, wo man vld.h inkludiert? Geht das auch wenn das Programm aus mehreren Projekten/DLLs besteht oder muss man in jedem Projekt vld.h inkludieren?

    1. Ja
    2. Die Datei muss einmal pro EXE/DLL includiert werden.


Anmelden zum Antworten