"nicht genügend Arbeitsspeicher"

  • VB.NET

Es gibt 12 Antworten in diesem Thema. Der letzte Beitrag () ist von RodFromGermany.

    "nicht genügend Arbeitsspeicher"

    Hallo,

    mein Mosaik-Bild-Programm läuft grundsätzlich (wenn auch erst rudimentär). Vorerst noch festgelegt für eine Auflösung 1600x900 mit 5x5 Bildteilen.

    Allerdings kommt nach ca 1,5 Minuten der Fehler "nicht genügend Arbeitsspeicher". Ich nutze Windows7-64 Bit mit 16 GB RAM.

    Quellcode

    1. Public Class Form1
    2. Public TeilBilder(5, 5)
    3. Public TeilBilderSwap
    4. Public ZufallX As Integer
    5. Public ZufallY As Integer
    6. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    7. Timer1.Interval = 500
    8. Timer1.Enabled = True
    9. Me.Height = SystemInformation.PrimaryMonitorSize.Height
    10. Me.Width = SystemInformation.PrimaryMonitorSize.Width
    11. PictureBox1.Height = Me.Height
    12. PictureBox1.Width = Me.Width
    13. Me.Hide()
    14. System.Threading.Thread.Sleep(500)
    15. For iw = 0 To 1280 Step 320
    16. For ih = 0 To 720 Step 180
    17. Dim b As New Bitmap(320, 180)
    18. Graphics.FromImage(b).CopyFromScreen(New Point(iw, ih), New Point(0, 0), b.Size)
    19. TeilBilder(iw / 320, ih / 180) = b
    20. Next
    21. Next
    22. Me.Show()
    23. Me.Opacity = 0
    24. End Sub
    25. Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
    26. Dim c As New Bitmap(1600, 900)
    27. Graphics.FromImage(c).CopyFromScreen(New Point(0, 0), New Point(0, 0), c.Size)
    28. Randomize()
    29. ZufallX = CInt(Int(4 * Rnd()))
    30. ZufallY = CInt(Int(5 * Rnd()))
    31. Graphics.FromImage(c).DrawImage(TeilBilder(ZufallX, ZufallY), New Point(320 * (ZufallX + 1), 180 * ZufallY))
    32. Graphics.FromImage(c).DrawImage(TeilBilder(ZufallX + 1, ZufallY), New Point(320 * ZufallX, 180 * ZufallY))
    33. TeilBilderSwap = TeilBilder(ZufallX, ZufallY)
    34. TeilBilder(ZufallX, ZufallY) = TeilBilder(ZufallX + 1, ZufallY)
    35. TeilBilder(ZufallX + 1, ZufallY) = TeilBilderSwap
    36. PictureBox1.Image = c
    37. Me.Opacity = 1
    38. Graphics.FromImage(c).Dispose()
    39. End Sub
    40. Private Sub Form1_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
    41. If e.KeyCode = Keys.Escape Then
    42. Application.Exit()
    43. End If
    44. End Sub
    45. End Class


    Ich bin davon ausgegangen, daß durch "Dispose" in Zeile 38 der Speicher immer wieder halbwegs freigegeben wird.




    Was kann ich tun?

    Gruß

    Zerberus
    Irre ich mich jetzt oder musst du nicht auch noch alle "Graphics.FromImage" Disposen? Jeder Aufruf erzeugt doch ein neues oder? Jedenfalls ist:

    C#-Quellcode

    1. Graphics g1 = Graphics.FromImage(bitmap);
    2. Graphics g2 = Graphics.FromImage(bitmap);
    3. Console.WriteLine(g1 == g2);
    = False.
    Ich denke c.Dispose() wird dir helfen.

    Ich habe diesen Eintrag anstelle der obigen Zeile 38 sowie zusätzlich zu Zeile 38 probiert, aber das Programm startet damit nicht mal richtig (statt Anzeige rotes Kreuz).

    Irre ich mich jetzt oder musst du nicht auch noch alle "Graphics.FromImage" Disposen?

    Siehe unteren Screenshot: habe dort in Zeile 20 Dispose eingefügt, nach einiger Zeit kommt der gezeigte Fehler...


    Wenn er c Disposed hauts ihm alles um die Ohren. Die Picbox braucht die Resource. Ich würd halt analysieren wo viel reinläuft(also Schritt für Schritt debuggen). Ich denk es knallt weil der Array zu lang is hab aber leider keine Lösung und keinen Pc.....

    Lg Mokki
    ​Smartnotr - ein intelligentes Notizprogramm
    zum Thread

    Erstell EINMAL ein Graphics.FromImage und das in einem Using-Block.
    Dann disposed es sich selbst. Du erstellst da 100 Instanzen, völliger Schwachsinn.

    Grüße
    "Life isn't about winning the race. Life is about finishing the race and how many people we can help finish the race." ~Marc Mero

    Nun bin ich also auch soweit: Keine VB-Fragen per PM! Es gibt hier ein Forum, verdammt!
    Ich hab das mal kurz umgeschrieben, so läuft das bei mir ganz gut.

    VB.NET-Quellcode

    1. Public TeilBilder(5, 5)
    2. Public TeilBilderSwap
    3. Public ZufallX As Integer
    4. Public ZufallY As Integer
    5. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    6. Timer1.Interval = 500
    7. Timer1.Enabled = True
    8. Me.Height = SystemInformation.PrimaryMonitorSize.Height
    9. Me.Width = SystemInformation.PrimaryMonitorSize.Width
    10. PictureBox1.Height = Me.Height
    11. PictureBox1.Width = Me.Width
    12. Me.Hide()
    13. System.Threading.Thread.Sleep(500)
    14. For iw = 0 To 1280 Step 320
    15. For ih = 0 To 720 Step 180
    16. Dim b As New Bitmap(320, 180)
    17. Graphics.FromImage(b).CopyFromScreen(New Point(iw, ih), New Point(0, 0), b.Size)
    18. TeilBilder(iw / 320, ih / 180) = b
    19. Next
    20. Next
    21. Me.Show()
    22. Me.Opacity = 0
    23. End Sub
    24. Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
    25. Dim c As New Bitmap(1600, 900)
    26. Using g As Graphics = Graphics.FromImage(c)
    27. g.CopyFromScreen(New Point(0, 0), New Point(0, 0), c.Size)
    28. End Using
    29. Randomize()
    30. ZufallX = CInt(Int(4 * Rnd()))
    31. ZufallY = CInt(Int(5 * Rnd()))
    32. Using g As Graphics = Graphics.FromImage(c)
    33. g.DrawImage(TeilBilder(ZufallX, ZufallY), New Point(320 * (ZufallX + 1), 180 * ZufallY))
    34. g.DrawImage(TeilBilder(ZufallX + 1, ZufallY), New Point(320 * ZufallX, 180 * ZufallY))
    35. End Using
    36. TeilBilderSwap = TeilBilder(ZufallX, ZufallY)
    37. TeilBilder(ZufallX, ZufallY) = TeilBilder(ZufallX + 1, ZufallY)
    38. TeilBilder(ZufallX + 1, ZufallY) = TeilBilderSwap
    39. If (PictureBox1.Image IsNot Nothing) Then
    40. PictureBox1.Image.Dispose()
    41. End If
    42. PictureBox1.Image = c
    43. Me.Opacity = 1
    44. End Sub
    45. Private Sub Form1_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
    46. If e.KeyCode = Keys.Escape Then
    47. Application.Exit()
    48. End If
    49. End Sub
    Vielen Dank,

    Using ist wohl die Lösung, Programm läuft seit einer halben Stunde ohne Absturz.

    Damit ich auch verstehe, was ich gemacht habe: Zuerst hatte ich ständig zusätzliche, neue Instanzen erstellt, welche trotz Dispose den Speicher füllten.

    Mit Using wird immer die gleiche Instanz benutzt und nur deren "Inhalt" überschrieben.

    Gruß

    Zerberus
    @Bluespide Und weshalb 2 Using?

    Grüße
    "Life isn't about winning the race. Life is about finishing the race and how many people we can help finish the race." ~Marc Mero

    Nun bin ich also auch soweit: Keine VB-Fragen per PM! Es gibt hier ein Forum, verdammt!

    Zerberus schrieb:

    Mit Using wird immer die gleiche Instanz benutzt und nur deren "Inhalt" überschrieben.

    Das hat nichts mit using zu tun.


    VB.NET-Quellcode

    1. Using g As Graphics = Graphics.FromImage(c)
    2. g.CopyFromScreen(New Point(0, 0), New Point(0, 0), c.Size)
    3. End Using

    ist nichts anderes als

    VB.NET-Quellcode

    1. Dim g As Graphics = Graphics.FromImage(c)
    2. Try
    3. g.CopyFromScreen(New Point(0, 0), New Point(0, 0), c.Size)
    4. Finally
    5. If g IsNot Nothing Then
    6. g.Dispose()
    7. End If
    8. End Try


    Der Using-Blockt bewirkt also nur, dass am Ende immer Dispose aufgerufen wird, um die Ressourcen freizugeben, sogar wenn in deinem Code eine Exception auftritt.

    Zerberus schrieb:

    VB.NET-Quellcode

    1. Randomize()
    Pack diese Zeile mal aus der Prozedur in die Klasse, der Zufallstahlengenerator sollte nur ein Mal gestartet werden.
    Wie schnell tickt Dein Timer?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    Zerberus schrieb:

    500
    Du musst natürlich sicherstellen, dass die Timer-Tick-Prozedur nicht ausgeführt werden darf, wenn sie vom letzten Tick noch nicht verlassen wurde.
    Die beste Methode dafür wäre Timer1.Stop() am Anfang und Timer1.Start() am Ende der Routine.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!