KSPModdingLibs/KSPCommunityFixes

KSP memory leaks

gotmachine opened this issue · 0 comments

KSP is leaking managed memory from various places, but mainly due to GameEvents delegates that are never removed.

Worst offender by far is the PartSet class which is holding Part references, is instantiated (massively) and owned by a bunch of classes (ShipConstruct, Vessel, Part...) and is subscribing to 2 GameEvents. It attempts to unsubscribe from those GameEvents with a GC finalizer, which obviously doesn't work since the GameEvents subscriptions are preventing those objects from being collected in the first place.

There are many other offenders, with the end result being that all Vessel or Part ever instantiated will actually never be collected. Since those are rather top level objects themself holding a rather large reference tree, the end result is an ever ballooning heap allocated memory. On average, for a stock game (figures will likely be much higher with mods), every part ever instantiated will cause a permanent leak of ~0.2MB.

As shown in the following table, quick-loading the same flight scene with a ~150 parts vessel causes a leak of about ~30 MB per scene switch. After 17 scene switches, about 450 MB of memory is forever lost.

Scene load # GameEvents callbacks (Stock) GameEvents callbacks (KSPCF) Allocated heap (MB) (Stock) Allocated heap (MB) (KSPCF) Leaks (MB) (Stock) Leaks (MB) (KSPCF)
1 3142 1020 742,0 783,5
2 5268 1024 805,1 856,4
3 7394 1046 834,2 878,8 29,1 22,3
4 9520 1032 850,5 882,9 16,3 4,1
5 11646 1036 877,8 874,6 27,3 -8,2
6 13772 1040 906,5 852,6 28,7 -22,1
7 15898 1044 933,7 858,9 27,2 6,3
8 18024 1048 966,9 849,7 33,2 -9,2
9 20150 1052 1012,4 880,8 45,6 31,2
10 22276 1056 1090,6 850,4 78,1 -30,4
11 24402 1060 1121,3 810,7 30,7 -39,8
12 26528 1064 1150,0 827,2 28,7 16,6
13 28654 1068 1179,6 849,1 29,7 21,8
14 30780 1072 1212,4 891,1 32,8 42,1
15 32906 1076 1239,0 850,4 26,6 -40,7
16 35032 1080 1270,8 849,4 31,7 -1,0
17 37158 1084 1301,5 850,0 30,7 0,6

Note that aside from increasing memory usage, this also cause performance degradation :

  • Heap will become more fragmented and GC collections will take longer.
  • "Dead" GameEvents delegate are still called, slowing down event processing and potentially causing weird side issues.

The KSPCF MemoryLeaks patch add a callback when a scene is exited, and walks through all GameEvents delegates to remove entries whose owner is a destroyed UnityEngine.Object derivative. There are just to many cases of leaked GameEvents, so instead of trying to fix every one of them, this is a cheap way to work around the whole issue. Moreover, this will also cover leaks coming from plugins (which I suspect are even worse offenders than stock).

A specific handling is added for the PartSet GameEvents leaks, since the class doesn't derive from UnityEngine.Object.

The patch also implement various concrete fixes for a bunch of non-GameEvents related leaks, mainly caused by static fields misuse and lazyness.