xamarin / xamarin-macios

.NET for iOS, Mac Catalyst, macOS, and tvOS provide open-source bindings of the Apple SDKs for use with .NET managed languages such as C#
Other
2.47k stars 512 forks source link

Overriding method in bounded class causing memory leak #8321

Open nrudnyk opened 4 years ago

nrudnyk commented 4 years ago

As the title stands, we've faced memory leak, caused by overriding bounded class. Setup we have:

Everything is described and attached below. If you need more information, or anything else I can help to reproduce, found the reason - let me know.

Steps to Reproduce

  1. Run the TestProjectConsumer app
  2. Push ViewController via pressing button.
  3. Press < back to pop back
  4. Memory leak.

Spikes on the memory graph reflects each time view controller was pushed. Please see some screenshots under the spoiler:

Screenshot 2020-04-08 at 14 24 06 Screenshot 2020-04-08 at 14 48 47 Screenshot 2020-04-08 at 14 24 24

Expected Behavior

  1. Push ViewController with class
  2. Go back(Pop) from it
  3. Memory is free

Actual Behavior

  1. Push ViewController with class
  2. Go back(Pop) from it
  3. There's happens to be a leak

Environment

``` === Visual Studio Community 2019 for Mac === Version 8.5.2 (build 13) Installation UUID: 48f6a3ad-d84d-4db4-a6f3-21b06c1343e6 GTK+ 2.24.23 (Raleigh theme) Xamarin.Mac 6.14.1.39 (d16-5 / 30e8706b4) Package version: 608000123 === Mono Framework MDK === Runtime: Mono 6.8.0.123 (2019-10/1d0d939dc30) (64-bit) Package version: 608000123 === Roslyn (Language Service) === 3.5.0-beta4-20125-04+1baa0b3063238ed752ad1f0368b1df6b6901373e === NuGet === Version: 5.4.0.6315 === .NET Core SDK === SDK: /usr/local/share/dotnet/sdk/3.1.200/Sdks SDK Versions: 3.1.200 3.1.102 MSBuild SDKs: /Library/Frameworks/Mono.framework/Versions/6.8.0/lib/mono/msbuild/Current/bin/Sdks === .NET Core Runtime === Runtime: /usr/local/share/dotnet/dotnet Runtime Versions: 3.1.2 2.1.16 === Xamarin.Profiler === Version: 1.6.13.11 Location: /Applications/Xamarin Profiler.app/Contents/MacOS/Xamarin Profiler === Updater === Version: 11 === Xamarin.Android === Not Installed === Microsoft Mobile OpenJDK === Java SDK: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home 1.8.0_212-release Android Designer EPL code available here: https://github.com/xamarin/AndroidDesigner.EPL === Android SDK Manager === Version: 16.5.0.39 Hash: 6fb4c79 Branch: remotes/origin/d16-5 Build date: 2020-04-02 00:16:12 UTC === Android Device Manager === Version: 16.5.0.71 Hash: 49194e8 Branch: remotes/origin/d16-5~1 Build date: 2020-04-02 00:16:32 UTC === Xamarin Inspector === Version: 1.4.3 Hash: db27525 Branch: 1.4-release Build date: Mon, 09 Jul 2018 21:20:18 GMT Client compatibility: 1 === Apple Developer Tools === Xcode 11.3.1 (15715) === Xamarin.Mac === Version: 6.16.0.13 (Visual Studio Community) Hash: b75deaf82 Branch: d16-5-xcode11.4 Build date: 2020-04-01 21:33:18-0400 === Xamarin.iOS === Version: 13.16.0.13 (Visual Studio Community) Hash: b75deaf82 Branch: d16-5-xcode11.4 Build date: 2020-04-01 21:33:19-0400 === Xamarin Designer === Version: 16.5.0.471 Hash: 35aa4889d Branch: remotes/origin/d16-5 Build date: 2020-02-25 00:52:08 UTC === Build Information === Release ID: 805020013 Git revision: 90b766333e562018f979de04293c097898dafdf4 Build date: 2020-04-02 10:35:07-04 Build branch: release-8.5 Xamarin extensions: 90b766333e562018f979de04293c097898dafdf4 === Operating System === Mac OS X 10.15.3 Darwin 19.3.0 Darwin Kernel Version 19.3.0 Thu Jan 9 20:58:23 PST 2020 root:xnu-6153.81.5~1/RELEASE_X86_64 x86_64 ```

Build Logs

``` Building TestProjectConsumer (Debug|iPhone) Build started 4/8/2020 3:50:43 PM. __________________________________________________ Project "/Users/nrudnyk/Downloads/TestProjectAndBindingLib/TestProjectBindingLib/TestProjectBindingLib/TestProjectBindingLib.csproj" (Build target(s)): Target _GenerateBindings: Skipping target "_GenerateBindings" because all output files are up-to-date with respect to the input files. Target _CompressNativeFrameworkResources: Skipping target "_CompressNativeFrameworkResources" because all output files are up-to-date with respect to the input files. Target _CompressObjCBindingNativeFrameworkResources: Skipping target "_CompressObjCBindingNativeFrameworkResources" because it has no inputs. Target _GenerateCompileInputs: /Library/Frameworks/Mono.framework/Versions/6.8.0/lib/mono/msbuild/Current/bin/Microsoft.Common.CurrentVersion.targets(3276,5): warning MSB9004: ManifestResourceWithNoCulture item type is deprecated. Emit EmbeddedResource items instead, with metadata WithCulture='false', Type='Resx', and optional LogicalName. Done building target "_GenerateCompileInputs" in project "TestProjectBindingLib.csproj". Target GenerateTargetFrameworkMonikerAttribute: Skipping target "GenerateTargetFrameworkMonikerAttribute" because all output files are up-to-date with respect to the input files. Target CoreCompile: Skipping target "CoreCompile" because all output files are up-to-date with respect to the input files. Target CopyFilesToOutputDirectory: TestProjectBindingLib -> /Users/nrudnyk/Downloads/TestProjectAndBindingLib/TestProjectBindingLib/TestProjectBindingLib/bin/Debug/TestProjectBindingLib.dll Done building project "TestProjectBindingLib.csproj". __________________________________________________ Project "/Users/nrudnyk/Downloads/TestProjectAndBindingLib/TestProjectBindingLib/TestProjectConsumer/TestProjectConsumer.csproj" (Build target(s)): Target _BeforeCoreCompileInterfaceDefinitions: Skipping target "_BeforeCoreCompileInterfaceDefinitions" because all output files are up-to-date with respect to the input files. Target _CoreCompileInterfaceDefinitions: Skipping target "_CoreCompileInterfaceDefinitions" because all output files are up-to-date with respect to the input files. Target _BeforeCoreCompileImageAssets: Skipping target "_BeforeCoreCompileImageAssets" because all output files are up-to-date with respect to the input files. Target _CoreCompileImageAssets: Skipping target "_CoreCompileImageAssets" because all output files are up-to-date with respect to the input files. Target _CoreCompileColladaAssets: Skipping target "_CoreCompileColladaAssets" because it has no inputs. Target _BeforeCoreCompileSceneKitAssets: Skipping target "_BeforeCoreCompileSceneKitAssets" because it has no inputs. Target _BeforeCoreCompileSceneKitAssets: Skipping target "_BeforeCoreCompileSceneKitAssets" because it has no inputs. Target _BeforeCoreCompileSceneKitAssets: Skipping target "_BeforeCoreCompileSceneKitAssets" because it has no inputs. Target _CoreCompileSceneKitAssets: Skipping target "_CoreCompileSceneKitAssets" because it has no inputs. Target _BeforeCompileTextureAtlases: Skipping target "_BeforeCompileTextureAtlases" because it has no inputs. Target _BeforeCompileTextureAtlases: Skipping target "_BeforeCompileTextureAtlases" because it has no inputs. Target _BeforeCompileTextureAtlases: Skipping target "_BeforeCompileTextureAtlases" because it has no inputs. Target _CoreCompileTextureAtlases: Skipping target "_CoreCompileTextureAtlases" because it has no inputs. Target _BeforeCompileCoreMLModels: Skipping target "_BeforeCompileCoreMLModels" because all output files are up-to-date with respect to the input files. Target _CoreCompileCoreMLModels: Skipping target "_CoreCompileCoreMLModels" because all output files are up-to-date with respect to the input files. Target _CoreOptimizePngImages: Skipping target "_CoreOptimizePngImages" because it has no outputs. Target _CoreOptimizePropertyLists: Skipping target "_CoreOptimizePropertyLists" because it has no inputs. Target _CoreOptimizeLocalizationFiles: Skipping target "_CoreOptimizeLocalizationFiles" because it has no inputs. Target _GetProjectReferenceTargetFrameworkProperties: __________________________________________________ Project "/Users/nrudnyk/Downloads/TestProjectAndBindingLib/TestProjectBindingLib/TestProjectConsumer/TestProjectConsumer.csproj" is building "/Users/nrudnyk/Downloads/TestProjectAndBindingLib/TestProjectBindingLib/TestProjectBindingLib/TestProjectBindingLib.csproj" (GetTargetFrameworks target(s)): Target ResolveProjectReferences: __________________________________________________ Project "/Users/nrudnyk/Downloads/TestProjectAndBindingLib/TestProjectBindingLib/TestProjectConsumer/TestProjectConsumer.csproj" is building "/Users/nrudnyk/Downloads/TestProjectAndBindingLib/TestProjectBindingLib/TestProjectBindingLib/TestProjectBindingLib.csproj" (GetTargetPath target(s)): __________________________________________________ Project "/Users/nrudnyk/Downloads/TestProjectAndBindingLib/TestProjectBindingLib/TestProjectConsumer/TestProjectConsumer.csproj" is building "/Users/nrudnyk/Downloads/TestProjectAndBindingLib/TestProjectBindingLib/TestProjectBindingLib/TestProjectBindingLib.csproj" (GetNativeManifest target(s)): Target GenerateTargetFrameworkMonikerAttribute: Skipping target "GenerateTargetFrameworkMonikerAttribute" because all output files are up-to-date with respect to the input files. Target CoreCompile: Skipping target "CoreCompile" because all output files are up-to-date with respect to the input files. Target _CopyFilesMarkedCopyLocal: Touching "/Users/nrudnyk/Downloads/TestProjectAndBindingLib/TestProjectBindingLib/TestProjectConsumer/obj/iPhone/Debug/TestProjectConsumer.csproj.CopyComplete". Target GetCopyToOutputDirectoryItems: Target _GetCopyToOutputDirectoryItemsFromTransitiveProjectReferences: __________________________________________________ Project "/Users/nrudnyk/Downloads/TestProjectAndBindingLib/TestProjectBindingLib/TestProjectConsumer/TestProjectConsumer.csproj" is building "/Users/nrudnyk/Downloads/TestProjectAndBindingLib/TestProjectBindingLib/TestProjectBindingLib/TestProjectBindingLib.csproj" (GetCopyToOutputDirectoryItems target(s)): Target CopyFilesToOutputDirectory: TestProjectConsumer -> /Users/nrudnyk/Downloads/TestProjectAndBindingLib/TestProjectBindingLib/TestProjectConsumer/bin/iPhone/Debug/TestProjectConsumer.exe Target _DetectSigningIdentity: Detected signing identity: Code Signing Key: "Apple Development: Nazar Rudnyk (A466QN3HLS)" (B5ED1E24A0A5D546161170215274D5DA2AE2C48C) Provisioning Profile: "iOS Team Provisioning Profile: *" (37edf304-158d-44d0-b2dd-1aff4050119e) Bundle Id: test.testprojectconsumer App Id: 4RZQG425FX.test.testprojectconsumer Target _CopyResourcesToBundle: Skipping target "_CopyResourcesToBundle" because all output files are up-to-date with respect to the input files. Target _DetectDebugNetworkConfiguration: DebugIPAddresses: Target _CompileAppManifest: Skipping target "_CompileAppManifest" because all output files are up-to-date with respect to the input files. Target _ParseExtraMtouchArgs: NoSymbolStrip Output: true NoDSymUtil Output: false Target _CompileToNative: Skipping target "_CompileToNative" because all output files are up-to-date with respect to the input files. Target _CompileITunesMetadata: Skipping target "_CompileITunesMetadata" because all output files are up-to-date with respect to the input files. Target _CopyITunesArtwork: Skipping target "_CopyITunesArtwork" because it has no inputs. Target _CopyAppExtensionsToBundle: Skipping target "_CopyAppExtensionsToBundle" because it has no inputs. Target _GenerateAppExtensionDebugSymbols: Directory "bin/iPhone/Debug/device-builds/iphone10.6-13.3.1/TestProjectConsumer.app/../.dSYM" doesn't exist. Skipping. Target _GenerateFrameworkDebugSymbols: Skipping target "_GenerateFrameworkDebugSymbols" because all output files are up-to-date with respect to the input files. Target _GenerateDebugSymbols: Skipping target "_GenerateDebugSymbols" because all output files are up-to-date with respect to the input files. Target _CodesignFrameworks: Skipping target "_CodesignFrameworks" because all output files are up-to-date with respect to the input files. Target _CodesignAppBundle: Skipping target "_CodesignAppBundle" because all output files are up-to-date with respect to the input files. Target _CodesignVerify: /usr/bin/codesign --verify -vvvv -R='anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.1] exists and (certificate leaf[field.1.2.840.113635.100.6.1.2] exists or certificate leaf[field.1.2.840.113635.100.6.1.4] exists)' bin/iPhone/Debug/device-builds/iphone10.6-13.3.1/TestProjectConsumer.app bin/iPhone/Debug/device-builds/iphone10.6-13.3.1/TestProjectConsumer.app: valid on disk bin/iPhone/Debug/device-builds/iphone10.6-13.3.1/TestProjectConsumer.app: satisfies its Designated Requirement bin/iPhone/Debug/device-builds/iphone10.6-13.3.1/TestProjectConsumer.app: explicit requirement satisfied Build succeeded. /Library/Frameworks/Mono.framework/Versions/6.8.0/lib/mono/msbuild/Current/bin/Microsoft.Common.CurrentVersion.targets(3276,5): warning MSB9004: ManifestResourceWithNoCulture item type is deprecated. Emit EmbeddedResource items instead, with metadata WithCulture='false', Type='Resx', and optional LogicalName. 1 Warning(s) 0 Error(s) Time Elapsed 00:00:01.14 ========== Build: 2 succeeded, 0 failed, 0 up-to-date, 0 skipped ========== Build: 0 errors, 1 warning ```

Example Project (If Possible)

For the sake of simplicity, test project is simple view controller which push another one, with Native object inside it. This makes possible going back\forth to clearly see the leak.

TestProjectAndBindingLib.zip

I might miss something, if so - please let me know, any suggestions is greatly appreciated. Thank you in advance!

chamons commented 4 years ago

How are you measure memory usage / leaks?

Are you waiting on the garbage collector to collect the managed instances, or are you just hitting the button over and measuring?

Unlike ObjC, C# uses a garbage collector so memory isn't immediately freed when popping a controller.

nrudnyk commented 4 years ago

sure, I'm aware about GC, and guess it I was forcing collection on each controller pop (going back to the main view). It's not included in the test project, since it doesn't make any change.

To answer your question - using memory graph as well as instruments, which shows memory spikes, there's a screenshots attached in the OP

chamons commented 4 years ago

It is very helpful, in particular on tricky issues like "memory leaks", where false positives are exceptionally easy to find, for the sample to accurately show how you are measuring things.

Can you please attach a sample with whatever memory management techniques you use to handle garbage collection and collect your number?

For every actual reported memory leak I see 5 where people misunderstand what they are measuring, GCs make things less obvious than one would first expect.

nrudnyk commented 4 years ago

Hello Chris,

The problem isn't in the garbage collection, the issue is that it works fine If no methods were overridden, and doesn't it there some. There's no fancy techniques to be used in this simple project, everything is disposed, events' are unsubscribed etc... this test project contains only 2 view controllers and native class with overridden method, and that's only to be able to showcase increasing memory...

I'm trying to think simple here, there are 2 cases for the native class used:

  1. no overrides
  2. there is an override for some of it's methods

First one, works with no problems, and second causes increasing of memory usage each time.

I understand, that there are a lot of false positive reports, and that's why prepared simple project right in the OP, so you can just "click and run" it, and see the problem i'm talking about. I'm not saying that's there is a bug or an issue in Xamarin.iOS, but there's something going on related to binding's generator and overrides. Also, Ш'm not omit the fact, that I might miss something, but the project is very very simple, so it's either I'm doing something wrong, or there's the issue in "under the hood" code. Hence - asking here.

But anyways, since you asking how I measure, I decided to slim the project, and include GC code. You can find it below: Slim-Test-Project.zip

In this example, there's only appDelegate, with simple code (2 cases)

  1. One uses object with overriden method
  2. Other don't use it.

Since c# collector doesn't know about native object, and only know about references, I used XCode to check memory. So here's what I did for each case:

Case with override:

Screenshot 2020-04-09 at 11 13 20

Case without override:

Screenshot 2020-04-09 at 11 16 40

In addition, you can find data from the GC.GetTotalMemory(false) in console, which looks strange, because for some reason c# memory is increasing after GC.Collect()

Hope that helps and you might be able to bring some insights about what's wrong with this example.

Thanks in advance!

nrudnyk commented 4 years ago

@chamons is there anything else I can do, so you can let me know what am I missing here?

chamons commented 4 years ago

Taking a look into this is on my backlog plate, we've just been a bit swamped.

nrudnyk commented 4 years ago

@chamons hello. Did you have a chance to look into this?

chamons commented 4 years ago

No, I was not able to make much progress on it recently. Given the soon release of Xcode 12 Beta it may be awhile.

Apologies.

nrudnyk commented 4 years ago

maybe there's some timetable or deadline, so we are aware of what to expect...?

chamons commented 4 years ago

While many problems can be resolved over github issues given sufficient time, they are prioritized based upon broad customer impact.

Because of this, it is not possible to lay out a timetable or deadline on when we'll be able to get back to looking on this, specially with Xcode 12 work beginning this summer.

If you are eligible for business supports requests with Microsoft, you can consider submitting a support request to the Xamarin support team.

andyb1979 commented 4 years ago

+1 for this issue. It's affecting our customers at SciChart - a fast iOS and Xamarin realtime chart control.

If we get customer issues reporting can we point them here to log / vote for the issue?

nrudnyk commented 4 years ago

hello @chamons, since XCode 12 is released already, maybe there's a time to look into this issue?

nrudnyk commented 3 years ago

any updates with regards to this issue?