2013-08-15 18 views
6

Ich habe ein ziemlich großes Programm, das im Allgemeinen wunderbar läuft, aber wahnsinnige Mengen an Speicher verwendet, um zu laufen. Dies ist ein Ansatz des maschinellen Lernens, der viele Daten sammelt und daher im Allgemeinen in Ordnung ist, aber der Speicher wächst und wächst sehr schnell, auch nachdem alle Daten gesammelt wurden. Daher habe ich valgrind massif verwendet, um herauszufinden, was falsch läuft. Der obere Teil des Massivs Haufen Baum sieht wie folgt aus:Mein Speicher ist nicht frei

99.52% (60,066,179B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc. 
->43.50% (26,256,000B) 0x439785: Image::Image(SDL_Surface*) (Image.cpp:95) 
| ->43.50% (26,256,000B) 0x437277: EncodedFeature::forwardPass() (EncodedFeature.cpp:65) 
.... 

Also dachte ich, hmm, vielleicht das konstruierte Bild ist nicht free'd, aber nicht:

void EncodedFeature::forwardPass() 
{ 
    // Get image: 
    Image* img = new Image(screen); 

    // Preprocess: 
    if(preprocessor) 
     preprocessor->process(img); 

    // Do forward pass: 
    encoder->encode(img, features); 

    delete img; 
} 

So zum Bild Konstruktor:

Image::Image(SDL_Surface* surface) 
{ 
    this->width = surface->w; 
    this->height = surface->h; 
    pixels = new int*[width]; 
    for(int i = 0; i < width; i++) 
     pixels[i] = new int[height]; 

    for(int x = 0; x < surface->w; x++) 
     for(int y = 0; y < surface->h; y++) 
      pixels[x][y] = getPixelFromSDLSurface(surface, x, y); 
} 

ordnet einfach eine Pixelanordnung, die später im destructor auf befreit wird:

Image::~Image() 
{ 
    if(!pixels) 
     return; 

    for(int x = 0 ; x < width; x++) 
     delete[] pixels[x]; 

    delete[] pixels; 
} 

So der letzte Schuldige:

Uint32 Image::getPixelFromSDLSurface(SDL_Surface *surface, int x, int y) 
{ 
    if(!surface) 
     return 0; 

    // Got this method from http://www.libsdl.org/cgi/docwiki.fcg/Pixel_Access 
    int bpp = surface->format->BytesPerPixel; 
    /* Here p is the address to the pixel we want to retrieve */ 
    Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp; 

    switch(bpp) { 
    case 1: 
     return *p; 
     break; 

    case 2: 
     return *(Uint16 *)p; 
     break; 

    case 3: 
     if(SDL_BYTEORDER == SDL_BIG_ENDIAN) 
      return p[0] << 16 | p[1] << 8 | p[2]; 
     else 
      return p[0] | p[1] << 8 | p[2] << 16; 
     break; 

    case 4: 
     return *(Uint32 *)p; 
     break; 

    default: 
     return 0;  /* shouldn't happen, but avoids warnings */ 
    } 
} 

Wie im Kommentar erwähnt, habe ich, dass man aus dem SDL Wiki, so dass ich hoffe, es ist nichts da undicht ist. bpp in meinem Fall ist eigentlich immer 1, also gibt es nur die int an Adresse p, die für mich nicht undicht klingt.

Ich bin am Ende meiner Weisheit hier. Kann jemand darüber nachdenken, wohin die Erinnerung ging? Ich meine, Massive Punkte speziell auf den Bildkonstruktor, aber ich kann nichts falsches dort sehen ...

Vielen Dank für das Betrachten meines Problems!

Max


beantworten Ihre Kommentare:

Sie haben Recht, ich habe nicht img als Zeiger benötigen. Ich komme von einem Java-Hintergrund, also möchte ich nur alles, um Zeiger zu sein :) änderte es, aber half nicht.

Linie 95 ist innerhalb der ersten for-Schleife des Konstrukteurs: pixels[i] = new int[height];

In einem der Preprozessoren ich tun die Größe des Bildes, aber ich diese meine Reset-Funktion aufrufen, die sicherstellen sollten, dass die alte Array gelöscht wird:

void Image::reset(int width, int height) 
{ 
    if(pixels) 
    { 
     // Delete old image: 
     for(int x = 0 ; x < width; x++) 
      delete[] pixels[x]; 

     delete[] pixels; 
    } 

    this->width = width; 
    this->height = height; 
    pixels = new int*[width]; 
    for(int i = 0; i < width; i++) 
     pixels[i] = new int[height]; 
} 

Nach dem ich die Pixelwerte aufzufüllen ...

keine Ausnahmen überall geworfen werden.

Wo würden Sie vorschlagen, dass ich intelligente Zeiger verwende?

Danke für alle Antworten!

+6

Ich empfehle die Verwendung von Vektoren/Smartpointer/geeignete Alternative anstelle von 'new' und' delete'. – chris

+1

Gibt es irgendwelche Ausnahmen und das Löschen fehlt? – doctorlove

+1

Welche Zeile hat Nummer 95 in der Datei 'Image.cpp'? –

Antwort

6

Ich glaube nicht, dass Sie Pixel in Ihrer Bildklasse korrekt darstellen. Ich denke, Sie können eine einfache 1-dimensionale vector des richtigen vorzeichenlosen Typs (uint32_t?) Verwenden.

(im Kopfteil):

class Image { 
protected: 
    std::vector<uint32_t> pixels; 
    ... 
}; 

(in Implementierungsdatei)

size_t Image::offset(unsigned x, unsigned y) { 
    return (y * width) + x; 
} 

Image::Image(const SDL_Surface* surface) 
{ 
    width = surface->w; 
    height = surface->h; 
    pixels.reserve(width * height); 
    for(unsigned x = 0; x < width; x++) 
     for(unsigned y = 0; y < height; y++) 
      pixels[offset(x, y)] = getPixelFromSDLSurface(surface, x, y); 
} 

Ihr destructor würde dann komplett leer sein, da es nichts für ihn zu tun:

Image::~Image() { 
} 

Hinweis: Sie müssen die offset() Methode verwenden, um den richtigen Index inzu erhaltenfür ein beliebiges gegebenes x/y-Paar.

+0

Vielen Dank für all das, daran hätte ich selbst nie gedacht! Ich habe es gerade ausprobiert und es scheint zu funktionieren. Führen Sie jetzt ein Experiment durch und schauen Sie sich die Speichernutzung an. Nur Fehler, den ich gefunden habe: Im Offset sollte es "Rückkehr (y * Breite) + x;" (umgekehrt) sein. Vielen Dank! – cpury

+0

@cpury Ja sollte es :) Ich werde die Antwort aktualisieren. – trojanfoe

1

Das wahrscheinlichste Szenario ist, dass Ihr Zurücksetzen oder Destruktor irgendwie nicht in einem Codepfad aufgerufen wird, wodurch dieser Speicher verloren geht.

Zum Glück hat C++ eine eingebaute Fähigkeit, solche Lecks zu verhindern: Es heißt vector.

Also zuerst machen Sie Ihre pixels wie folgt aussehen:

typedef std::vector<int> PixelCol; 
typedef std::vector<PixelCol> Pixels; 
Pixels pixels; 

Wählen Jetzt wir es im Konstruktor:

Image::Image(SDL_Surface* surface) 
: pixels(surface->w, PixelCol(surface->h)) 
{ 
    this->width = surface->w; 
    this->height = surface->h; 

    for(int x = 0; x < surface->w; x++) 
     for(int y = 0; y < surface->h; y++) 
      pixels[x][y] = getPixelFromSDLSurface(surface, x, y); 
} 

wir reset Schließlich aktualisieren:

void Image::reset(int width, int height) 
{ 
    Pixels(width, PixelCol(height)).swap(pixels); 
} 

Indem die Sprache verwaltet Ihr Gedächtnis, Sie eliminieren solche Erinnerungen vollständig Management Bug sin Ihren Code.

+0

Sieht ähnlich aus wie @trjanfoe's Antwort, also bin ich sicher, dass es auch mein Problem lösen würde! Vielen Dank! – cpury

Verwandte Themen