09. maj 2007 - 17:41Der 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?
Denne side indeholder artikler med forskellige perspektiver på Identity & Access Management i private og offentlige organisationer. Artiklerne behandler aktuelle IAM-emner og leveres af producenter, rådgivere og implementeringspartnere.
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 ... )
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().
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.
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).
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; }
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.
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?
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.