Avatar billede r.hegaard Nybegynder
09. maj 2007 - 17:41 Der er 10 kommentarer og
2 løsninger

Meget stort hukommelsesforbrug i C# billedefremvisningsprogram

Jeg har lavet et simpelt billedefremvisningsprogram i C#, som kan bruges til at gennemse en masse billeder. Programmet cacher, så der hele tiden er 80 billeder i hukommelsen af gangen. Hvis billederne er meget store, bliver de nedskaleret, når de læses ind i hukommelsen.
Derfor undrer det mig, hvorfor programmet bruger mere og mere hukommelse, jo flere billeder der har været læst ind, for jeg sørger hele tiden for, at der ikke er mere end 80 billeder i hukommelsen og jeg sørger for at dispose de gamle billeder.
Programmet kan bruge op til 700 MB RAM og 800 MB virtuel hukommelse.
Nogen der har en idé om, hvad jeg kan gøre, for at få mit program til at køre bedre?
Avatar billede Syska Mester
09. maj 2007 - 17:48 #1
Jeg ville bestemt mene at det ikke er det hele du får lavet en Dispose på ... 800 MB er jo helt vildt ....

// ouT

(Der findes også en function som laver en tvunget GarbegaCollection ... selvom den skulle køre automatisk kan det være en fordel af køre den selv nogen gange har jeg læst i en tidligere tråd hvis man har ram problemer ... )
Avatar billede r.hegaard Nybegynder
09. maj 2007 - 18:00 #2
Jeg har prøvet at kalde "System.GC.Collect();" hver gang jeg har ryddet op. Det ser desværre ikke ud til, at det hjæper noget særligt.
Avatar billede driis Nybegynder
09. maj 2007 - 18:48 #3
Det er vigtigt at du undgår at efterlade nogle udestående referencer til dine objekter.
Men hvis vi skal komme det nærmere, er vi nok nødt til at se noget kode.

Generelt kan det ikke anbefales selv at kalde GC.Collect().
Avatar billede r.hegaard Nybegynder
09. maj 2007 - 22:05 #4
buzzzz> Hvorfor vil du ikke mene, at jeg får det hele med dispose? Hvad skal jeg mere gøre?

driis> Jeg synes, at jeg har været påpasselig med ikke at efterlade referencer, men det kan da godt være, at jeg skal give det et ekstra tjek. Kan frameworket selv finde på at efterlade referencer på nogen måde?

I øvrigt kan jeg se ind i mellem, at hukommelsesforbruget falder drastisk. Så det må jo være, når GC kører. Det ser bare ud til, at det er sjældet at det sker. Seneste har jeg oplevet, at programmet gik helt i stå, fordi det var løbet tør for hukommelse.
Avatar billede driis Nybegynder
09. maj 2007 - 22:25 #5
Dispose sletter ikke hukommelsen der anvendes af et managed objekt. Den frigiver eventuelle unmanaged ressourcer, objektet måtte bruge (f.eks. lukker en fil).

Frameworket "efterlader" ikke referencer af sig selv. Men så længe der er nogle af dine objekter, der har nogen måde at "nå" objekterne på, vil de ikke blive garbage collected.

I øvrigt, hvilket format er dine billeder i, når de ligger i hukommelsen ?
Hvis det er rå bitmap data, fylder de typisk 4 byte/pixel. Hvis vi siger at det er taget med et nyere digitalkamera er det ikke umuligt at det er f.eks. billeder med 6Mpixel opløsning. Det giver cirka 4 * 6 * 10 ^ 6 = 24 MB per billede * 80 billeder = cirka 2 GB.

Eksemplet viser, at det ikke er umuligt, at hukommelsesforbruget faktisk er det reelle der skal bruges for at cache dine billeder i hukommelsen. Måske du skulle finde størrelsen på ét af dine billede-objekter, for at vurdere, om det er det, der sker.

Hvis det er tilfældet, kan du overveje kun at opbevare den komprimerede version af billedet i hukommelsen og/eller at mindske antallet af billeder i hukommelsen (Du har næppe brug for 80 billeder cachet i hukommelsen alligevel).
Avatar billede r.hegaard Nybegynder
10. maj 2007 - 21:17 #6
driis> Tak for svaret.
Jeg er klar over, at hvis jeg har referencer til objekterne, så bliver de ikke slettet. Men jeg mener bestemt, at jeg husker at slette alle referencer til mine billeder.
Jeg var godt klar over, at billederne ville fylder meget i hukommelsen og derfor har jeg også lavet, så de bliver skaleret ned når de bliver læst ind, hvis de er meget store.
Jeg var dog ikke klar over, at billederne fylder så meget som du skriver. Det er JPG billeder, som jeg læser ind i et bitmap objekt. Det mærkelige er, at hukommelsesforbruget stiger løbende, efter at jeg har haft læst mange billeder ind og slettet mange billeder igen. Det fylder altså ikke så meget i starten, selvom der også er læst mange billeder ind der.
Hvordan kan jeg opbevare den komprimerede version af billedet i hukommelsen?
I programmet viser jeg op til 25 billeder af gangen, derfor vil jeg gerne have cached en del billeder, så man ikke behøver at vente længe på billedindlæsning, hvis man bladrer hurtigt igennem billederne.

Det vil være noget besværligt at indsætte alt min kode her, men jeg vil lige prøve at liste det væsenligste, for at se, om det kan hjælpe med opklaring af problemet:

Jeg bruger følgende kode, når jeg læser billederne ind:
FileStream fs = filesInCurrentFolder[i].File.OpenRead();
Bitmap tmpBmp = new Bitmap(fs);
Bitmap bmp;
if (tmpBmp.Width > 2 * currentSize.Width || tmpBmp.Height > 2 * currentSize.Height)
{
  bmp = ResizeBitmap(tmpBmp, tmpBmp.Width / 2, tmpBmp.Height / 2);
}
else if (tmpBmp.Width > 1.5 * currentSize.Width || tmpBmp.Height > 1.5 * currentSize.Height)
{
  bmp = ResizeBitmap(tmpBmp, (int)(tmpBmp.Width / 1.5), (int)(tmpBmp.Height / 1.5));
}
else
{
  bmp = new Bitmap(tmpBmp);
}
if (filesInCurrentFolder[i].Rotation != 0)
{
  if (filesInCurrentFolder[i].Rotation == 90) bmp.RotateFlip(RotateFlipType.Rotate90FlipNone);
  if (filesInCurrentFolder[i].Rotation == 180) bmp.RotateFlip(RotateFlipType.Rotate180FlipNone);
  if (filesInCurrentFolder[i].Rotation == 270) bmp.RotateFlip(RotateFlipType.Rotate270FlipNone);
}
openFiles.Add(filesInCurrentFolder[i].File.FullName, bmp);
tmpBmp.Dispose();
fs.Dispose();

Jeg bruger følgende kode til at rydde op i billederne:
if (openFiles.Count >= 80)
{
  Dictionary<String, Image> openFilesNew = new Dictionary<string, Image>();
  for (int i = currentFilesStart; i < readUpTo; i++)
  {
      if (i < filesInCurrentFolder.Count && i >= 0)
      {
        if (openFiles.ContainsKey(filesInCurrentFolder[i].File.FullName))
        {
            openFilesNew.Add(filesInCurrentFolder[i].File.FullName, openFiles[filesInCurrentFolder[i].File.FullName]);
            openFiles.Remove(filesInCurrentFolder[i].File.FullName);
        }
      }
  }
  foreach (KeyValuePair<String,Image> var in openFiles)
  {
      if (var.Value != null)
      {
        var.Value.Dispose();
      }
  }
  openFiles.Clear();
  openFiles = openFilesNew;
}

Når jeg læser billederne ind i en PictureBox, gør jeg følgende:
if (openFiles.ContainsKey(filesInCurrentFolder[nextPicture].File.FullName))
{
  pictureBoxes[i].Image = openFiles[filesInCurrentFolder[nextPicture].File.FullName];
}

Når nogle billeder skal fjernes fra PictureBox'ene, gør jeg følgende:
for (int i = 0; i < pictureBoxes.Count; i++)
{
  pictureBoxes[i].Image = null;
}
Avatar billede r.hegaard Nybegynder
12. maj 2007 - 15:23 #7
driis> Har du ud fra ovenstående nogen idéer til, hvad der kan være galt?
Avatar billede driis Nybegynder
13. maj 2007 - 18:49 #8
Hvis der sker exceptions, får du ikke kaldt dispose. Men jeg tror ikke, at det er det; der er galt.

Koden er ikke thread-safe; men hvis du ikke bruger tråde, er det OK.

Jeg ville nok se på hvor meget filerne egentlig fylder i hukommelsen.
Hvis problemet rent faktisk ligger dér (det er jeg 90% sikker på, at det gør); så blot læs filen ind i hukommelsen, men undlad at instantiere Bitmap objektet, før du skal vise billedet.
Avatar billede sherlock Nybegynder
30. maj 2007 - 15:33 #9
En fodnote. GC.Collect() skal kaldes 2 gange før den rent faktisk rydder op. Første gang markerer den kun kandidater til at blive fjernet.
Avatar billede r.hegaard Nybegynder
02. februar 2008 - 22:01 #10
Det er vist ved at være tid, at jeg får lukket her. Jeg har ikke fået løst problemet, men jeg har heller ikke haft så meget tid til at kigge på det.
Lægger I alle et svar?
Avatar billede r.hegaard Nybegynder
12. februar 2008 - 17:57 #11
Ikke flere der vil svare?
Avatar billede sherlock Nybegynder
27. februar 2008 - 10:12 #12
Nix :)
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
IT-kurser om Microsoft 365, sikkerhed, personlig vækst, udvikling, digital markedsføring, grafisk design, SAP og forretningsanalyse.

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester