C# Memory leak

  • C#
  • .NET (FX) 4.5–4.8

Es gibt 5 Antworten in diesem Thema. Der letzte Beitrag () ist von Niko Ortner.

    C# Memory leak

    Moin zusammen,

    ich würde gerne mal eure Meinung zu folgender Problemstellung wissen:

    C#-Quellcode

    1. ​namespace ConsoleApp1 {
    2. class Program {
    3. static void Main(string[] args) {
    4. List<Action> actions = new List<Action>();
    5. Task.Run(() => {
    6. byte[] data = new byte[100_000_000];
    7. for (int i = 0; i < data.Length; i++) {
    8. data[i] = 1;
    9. }
    10. actions.Add(() => {
    11. data[44] = 22;
    12. });
    13. });
    14. while (true) {
    15. System.Threading.Thread.Sleep(2000);
    16. if (actions.Count > 0) {
    17. actions[0]();
    18. actions.RemoveAt(0);
    19. Console.WriteLine("Action done!");
    20. }
    21. GC.Collect();
    22. GC.WaitForPendingFinalizers();
    23. GC.Collect();
    24. }
    25. }
    26. }
    27. }


    Also meiner Meinung nach ist das (jetzt nicht gerade sinnvoller aber) valider C# Code. Jedoch bleibt das Array für immer stehen, also eigentlich ein Leck, obwohl ich nach dem entfernen der Action aus der Liste keine Referenzen mehr darauf habe. Ist das ein Leck im System oder habe ich da was übersehen?
    @Bluespide Die Frage ist doch, wo das Array angelegt wird. Für mich sieht das so aus, als ob das Array lokal in der Main()-Prozedur abgelegt ist, in einer Action-Instanz in jedem Falle nicht.
    Wenn dem so ist, wird es natürlich auch erst nach Beenden der Main()-Prozedur zerstört.
    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!

    Bluespide schrieb:

    in eine Methode
    Poste mal diesen Code.
    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!

    C#-Quellcode

    1. ​namespace ConsoleApp1 {
    2. class Program {
    3. static void Main(string[] args) {
    4. List<Action> actions = new List<Action>();
    5. Test t = new Test();
    6. t.TestMeth(actions);
    7. t = null;
    8. while (true) {
    9. System.Threading.Thread.Sleep(2000);
    10. if (actions.Count > 0) {
    11. actions[0]();
    12. actions.RemoveAt(0);
    13. Console.WriteLine("Action done!");
    14. }
    15. GC.Collect();
    16. GC.WaitForPendingFinalizers();
    17. GC.Collect();
    18. }
    19. }
    20. }
    21. class Test {
    22. public void TestMeth(List<Action> actions) {
    23. Task.Run(() => {
    24. byte[] data = new byte[100_000_000];
    25. for (int i = 0; i < data.Length; i++) {
    26. data[i] = 1;
    27. }
    28. actions.Add(() => {
    29. data[44] = 22;
    30. });
    31. });
    32. }
    33. }
    34. }
    Ich hab hier ein Testprogramm für dich:
    Spoiler anzeigen

    C#-Quellcode

    1. class Program
    2. {
    3. public static void PrintInfo(string Text)
    4. {
    5. Console.WriteLine("[{0}] {1}", System.Threading.Thread.CurrentThread.ManagedThreadId, Text);
    6. }
    7. private static byte[] data2 = null;
    8. static void Main(string[] args)
    9. {
    10. PrintInfo("Start");
    11. List<Action> actions = new List<Action>();
    12. Test t = new Test();
    13. t.TestMeth(actions);
    14. t = null;
    15. while (true)
    16. {
    17. PrintInfo("Waiting for input");
    18. PrintInfo("1: Show Actions");
    19. PrintInfo("2: Perform Action 0");
    20. PrintInfo("3: Collect");
    21. PrintInfo("4: Allocate Data 2 (large)");
    22. PrintInfo("5: Allocate Data 2 (small)");
    23. PrintInfo("6: Clear Data 2");
    24. switch (Console.ReadLine())
    25. {
    26. case "1":
    27. PrintInfo(actions.Count + " Actions to do");
    28. break;
    29. case "2":
    30. if (actions.Count > 0)
    31. {
    32. PrintInfo("Doing Action!");
    33. actions[0]();
    34. actions.RemoveAt(0);
    35. }
    36. else
    37. {
    38. PrintInfo("No Action");
    39. }
    40. break;
    41. case "3":
    42. GC.Collect();
    43. GC.WaitForPendingFinalizers();
    44. GC.Collect();
    45. break;
    46. case "4":
    47. try
    48. {
    49. data2 = new byte[100000000];
    50. for (int i = 0; i < data2.Length; i++)
    51. {
    52. data2[i] = 1;
    53. }
    54. }
    55. catch (OutOfMemoryException)
    56. {
    57. PrintInfo("Out Of Memory");
    58. }
    59. break;
    60. case "5":
    61. try
    62. {
    63. data2 = new byte[1000000000];
    64. for (int i = 0; i < data2.Length; i++)
    65. {
    66. data2[i] = 1;
    67. }
    68. }
    69. catch (OutOfMemoryException)
    70. {
    71. PrintInfo("Out Of Memory");
    72. }
    73. break;
    74. case "6":
    75. data2 = null;
    76. break;
    77. }
    78. }
    79. }
    80. }
    81. class Test
    82. {
    83. public void TestMeth(List<Action> actions)
    84. {
    85. Program.PrintInfo("Start Task");
    86. Task.Run(() => {
    87. Program.PrintInfo("Allocating Array 1");
    88. byte[] data = new byte[1000000000];
    89. Program.PrintInfo("Writing Array 1");
    90. for (int i = 0; i < data.Length; i++)
    91. {
    92. data[i] = 1;
    93. }
    94. Program.PrintInfo("Array 1 Done");
    95. actions.Add(() => {
    96. Program.PrintInfo("Use Array 1");
    97. data[44] = 22;
    98. });
    99. });
    100. }
    101. }

    Daneben den Task-Manager öffnen und im Details-Tab nach Arbeitsspeicher sortieren.
    Beim Debuggen hat Aktion 3 bei mir keinen Effekt, aber wenn ich die Exe im Release-Modus kompiliere und dann vom Explorer starte, dann funktioniert es problemlos.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils