Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[desktop] High memory consumption [Part 2] #95092

Closed
ivk1800 opened this issue Dec 11, 2021 · 75 comments
Closed

[desktop] High memory consumption [Part 2] #95092

ivk1800 opened this issue Dec 11, 2021 · 75 comments
Labels
a: desktop Running on desktop c: performance Relates to speed or footprint issues (see "perf:" labels) customer: going dependency: dart Dart team may need to help us engine flutter/engine repository. See also e: labels. found in release: 2.6 Found to occur in 2.6 found in release: 2.8 Found to occur in 2.8 has reproducible steps The issue has been confirmed reproducible and is ready to work on P1 High-priority issues at the top of the work list perf: memory Performance issues related to memory r: solved Issue is closed as solved team-engine Owned by Engine team

Comments

@ivk1800
Copy link
Contributor

ivk1800 commented Dec 11, 2021

continue of #90868

in multi modules project consumes too much memory. project contains 40 modules with included dart_code_metrics. I attach test project for reproduce.

Steps fo reproduce:

  1. download archive and extract
  2. open project in intellij idea
  3. open any dart file and enable dart for project
  4. run sync.sh
  5. close idea
  6. open again project
  7. wait until analizier done
  8. check system monitor
  9. check memory, memory grows
  10. wait until stop growing
  11. memory not released

dart_code_metrics: 4.8.1

Dart version 2.15.0, macOS 11.5.2 20G95 darwin-x64

image

HighMemoryConsumption.zip

memory.mov
@ivk1800
Copy link
Contributor Author

ivk1800 commented Dec 11, 2021

@scheglov @incendial

@incendial
Copy link

@ivk1800 we have an open issue in Dart Code Metrics repo and right now it looks like the problem is with the plugin, not the integration. But I'd really appreciate some help from the Dart team, just to be sure that the integration is solid and doesn't have memory leaks.

@incendial
Copy link

After some investigation with different plugin setups I'm not that sure that it's our code leaking. Definitely need some help with checking the integration memory consumption.

@darshankawar darshankawar added the in triage Presently being triaged by the triage team label Dec 13, 2021
@darshankawar
Copy link
Member

I tried the given zip and followed steps to replicate on latest master and stable on mac and can see CPU and memory consumed by dart, as below:

Screenshot 2021-12-13 at 2 19 43 PM
Screenshot 2021-12-13 at 2 19 58 PM

stable, master flutter doctor -v
[✓] Flutter (Channel stable, 2.8.0, on Mac OS X 10.15.4 19E2269 darwin-x64,
    locale en-GB)
    • Flutter version 2.8.0 at /Users/dhs/documents/fluttersdk/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision cf44000065 (10 hours ago), 2021-12-08 14:06:50 -0800
    • Engine revision 40a99c5951
    • Dart version 2.15.0

[✓] Android toolchain - develop for Android devices (Android SDK version 30)
    • Android SDK at /Users/dhs/Library/Android/sdk
    • Platform android-30, build-tools 30.0.3
    • ANDROID_HOME = /Users/dhs/Library/Android/sdk
    • Java binary at: /Users/dhs/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/202.7486908/Android
      Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
    • All Android licenses accepted.    

[✓] Xcode - develop for iOS and macOS
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 12.3, Build version 12C33
    • CocoaPods version 1.10.1

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 4.1)
    • Android Studio at /Users/dhs/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/202.7486908/Android
      Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build
      1.8.0_242-release-1644-b3-6915495)        

[✓] VS Code (version 1.57.1)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.21.0

[✓] Connected device (4 available)
    • Darshan's iphone (mobile)  • 21150b119064aecc249dfcfe05e259197461ce23 •
      ios            • iOS 14.4.1 18D61
    • iPhone 12 Pro Max (mobile) • A5473606-0213-4FD8-BA16-553433949729     •
      ios            • com.apple.CoreSimulator.SimRuntime.iOS-14-3 (simulator)
    • macOS (desktop)            • macos                                    •
      darwin-x64     • Mac OS X 10.15.4 19E2269 darwin-x64
    • Chrome (web)               • chrome                                   •
      web-javascript • Google Chrome 93.0.4577.82


• No issues found!

[[✓] Flutter (Channel master, 2.6.0-12.0.pre.1002, on Mac OS X 10.15.4 19E2269
    darwin-x64, locale en-GB)
    • Flutter version 2.6.0-12.0.pre.1002 at
      /Users/dhs/documents/fluttersdk/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 9de8849905 (3 hours ago), 2021-12-12 20:54:15 -0500
    • Engine revision f16c96c31f
    • Dart version 2.16.0 (build 2.16.0-85.0.dev)
    • DevTools version 2.9.1
    
[✓] Android toolchain - develop for Android devices (Android SDK version 30)
    • Android SDK at /Users/dhs/Library/Android/sdk
    • Platform android-30, build-tools 30.0.3
    • ANDROID_HOME = /Users/dhs/Library/Android/sdk
    • Java binary at: /Users/dhs/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/202.7486908/Android
      Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
    • All Android licenses accepted.    

[✓] Xcode - develop for iOS and macOS
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 12.5.1, Build version 12E507
    • CocoaPods version 1.10.1

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 4.1)
    • Android Studio at /Users/dhs/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/202.7486908/Android
      Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build
      1.8.0_242-release-1644-b3-6915495)        

[✓] VS Code (version 1.57.1)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.21.0

[[✓] Connected device (4 available)
    • Darshan's iphone (mobile)  • 21150b119064aecc249dfcfe05e259197461ce23 •
      ios            • iOS 14.4.1 18D61
    • iPhone 12 Pro Max (mobile) • A5473606-0213-4FD8-BA16-553433949729     •
      ios            • com.apple.CoreSimulator.SimRuntime.iOS-14-3 (simulator)
    • macOS (desktop)            • macos                                    •
      darwin-x64     • Mac OS X 10.15.4 19E2269 darwin-x64
    • Chrome (web)               • chrome                                   •
      web-javascript • Google Chrome 93.0.4577.82


• No issues found!


@darshankawar darshankawar added a: desktop Running on desktop found in release: 2.6 Found to occur in 2.6 found in release: 2.8 Found to occur in 2.8 has reproducible steps The issue has been confirmed reproducible and is ready to work on perf: memory Performance issues related to memory c: performance Relates to speed or footprint issues (see "perf:" labels) dependency: dart Dart team may need to help us engine flutter/engine repository. See also e: labels. and removed in triage Presently being triaged by the triage team labels Dec 13, 2021
@darshankawar
Copy link
Member

/cc @mraleph

@mraleph
Copy link
Member

mraleph commented Dec 13, 2021

AFAIK plugins run in isolates so I would not be surprised that memory jumps considerably when you load additional analyser plugins. AST representation is likely duplicated between main analyser isolate and each plugin.

@bwilkerson @scheglov should know how to diagnose this further.

@scheglov
Copy link
Contributor

OK, I also see high memory consumption, by the plugins isolate.
image

@scheglov
Copy link
Contributor

I cannot analyze the heap snapshot when all work is done on plugins, 10 GB seems too much. But a partial snapshot shows some contrast with the main isolate.

Plugins.
image

Main isolate.
image

@scheglov
Copy link
Contributor

Looking into individual AnalysisDriver instance shows that both have approximately the same size for FileSystemState, but plugins have much more. Specifically:

  1. Each AnalysisDriver in plugins has its own MemoryByteStore, which means that each of them analysis Flutter independently. This wastes both CPU and memory. Ideally we want to use one shared ByteStore instance.
  2. Each driver also has a large portion of the heap used by the element model. This might be something that I affected recently - we started to keep LibraryContext instances even after analysis is done, to support better code completions. But you don't need this in plugins. Or it could also be because of (1) - we have to resynthesize portions of the element model to link more element models, even though we could just resynthisize all of it from bytes without linking at all.

image

Main:
image

@bwilkerson
Copy link
Contributor

... to support better code completions. But you don't need this in plugins.

Not sure whether it matters, but plugins are allowed to also contribute to code completions.

@scheglov
Copy link
Contributor

It probably does not matter for plugins. We have to keep element models to ensure that element instances are mostly the same, so we can use Expando to cache corresponding completion suggestions, which is important only when we suggest a lot - like a few thousand classes in Flutter. Building a few suggestions for expr.^ context should not be very expensive anyway.

@scheglov
Copy link
Contributor

Just clearing the libraries contexts does not help at all. We still have references to these elements via cached results for "priority" files. But I don't think this is a recent change in the analyzer. If the plugin always used to configure AnalysisDrivers to set these priority files, we should have always had such high memory consumption. But maybe this issue existed always.

We cache resolved units for priority files because a priority file is a file that the user has open in the IDE. So, as he moves the caret in the file, we want to produce Quick Assists, and don't want to resolve file each time. It is not obvious to me that plugins need such caching.

image
image

@bwilkerson
Copy link
Contributor

So, as he moves the caret in the file, we want to produce Quick Assists, and don't want to resolve file each time. It is not obvious to me that plugins need such caching.

Probably the most common use cases for plugins are to provide custom lints and custom fixes. It would be better if the plugin didn't need to re-resolve the priority file when the user clicks in a highlighted error range to get quick fixes.

@scheglov
Copy link
Contributor

If the plugin does provide quick fixes - yes. But if only diagnostics - no.

Disabling caching priority files in plugins makes it somewhat better, but not totally. We still use a lot of heap, but most of it is garbage.

Before GC:
image

After GC:
image

It is unfortunate that VM continues to hold most of this heap.

@scheglov
Copy link
Contributor

Using single static MemoryByteStore, shared between all drivers created in the plugin improves heap usage, and also makes it much, much faster (no number). Which is not surprising - we don't have to link large libraries of Flutter 40 times, and usually do it only once.

Before GC:
image

After GC:
image

image

@incendial
Copy link

incendial commented Dec 14, 2021

@scheglov first of all, thank you so much for the investigation!

Using single static MemoryByteStore, shared between all drivers created in the plugin improves heap usage, and also makes it much, much faster (no number). Which is not surprising - we don't have to link large libraries of Flutter 40 times, and usually do it only once.

Could you please share how to achieve this with the current API? Right now we use ContextBuilder(resourceProvider: resourceProvider); to instantiate the context, which doesn't take anything else and is defined here. Also, how to make MemoryByteStore sharable between all instances, since the plugin is a separate dependency per package? Does they have some shared code too?

Another question - is it possible to share the MemoryByteStore with the main process?

Disabling caching priority files in plugins makes it somewhat better, but not totally. We still use a lot of heap, but most of it is garbage.

I don't know, if it's related or not, but right now we need to use hacks in order to highlight the errors we send to the analyzer in opened files. It was copied from another plugin. Maybe there is a better way to do it?

@m-skolnick
Copy link

@scheglov For what it's worth. I noticed the high memory consumption early this year. It's not something new to a recent release.

@scheglov
Copy link
Contributor

I don't have great understanding of plugins and the APIs that they should be based on. I will leave it to @bwilkerson regarding plugins API. I just want to note that once you step inside the non-API territory with DriverBasedAnalysisContext, you could just as well started using AnalysisContextCollectionImpl that does allow you to set the byteStore.

As for which implementation of ByteStore to use, I used MemoryByteStore just for hack, but it is "infinite", will never flush, which you don't want to do long term. So, MemoryCachingByteStore(NullByteStore) is probably optimal. Using EvictingFileByteStore might let reusing some data between restarts, but might have its own complications.

Nothing of this is API of course.

My understanding of why you need these hacks is that when a file (that was addFile before) is affected by a changeFile, you want to get its resolved unit and produce additional diagnostics. This looks awfully like a lint rule. Which raises a question - why all this is not in linter?

If we stay in a plugin, a more direct way to achieve this is to a callback to AnalysisDriver that is invoked after a file resolution, e.g. void Function(List<CompilationUnit>) onResolvedLibrary. Then results will still contain only ErrorsResults, but this is fine - you will not use it.

@incendial
Copy link

incendial commented Dec 15, 2021

I just want to note that once you step inside the non-API territory with DriverBasedAnalysisContext, you could just as well started using AnalysisContextCollectionImpl that does allow you to set the byteStore

Unfortunately, we use implementation imports just because there is no other way to create a driver without them. We'd be really happy to use public API instead, but I got your point, I think we can at least give it a try in order to resolve current performance issue.

As for which implementation of ByteStore to use, I used MemoryByteStore just for hack, but it is "infinite", will never flush, which you don't want to do long term. So, MemoryCachingByteStore(NullByteStore) is probably optimal. Using EvictingFileByteStore might let reusing some data between restarts, but might have its own complications.

Thank you, do we need to define it outside of createAnalysisDriver method?

Nothing of this is API of course.

But why? Are you not interested in investing into plugins API or it's just so low on the priority list and you don't have time for it? I mean, I understand that it's barely used right now, but the potential of custom lint rules for a specific team requirements is underestimated right now.

Which raises a question - why all this is not in linter?

You mean, why the functionality that the package provides is not added to the linter package directly, instead of a separate package with custom lint rules?

@DanTup
Copy link
Contributor

DanTup commented Jul 18, 2022

Are you talking about some specific logging?

I meant if you're able to run the plugin from source, could you add something like File('/foo/bar.txt').writeAsString('info here', mode = FileMode.append); into your code where you're sending these empty arrays to understand why it's happening?

I haven't tested it, but it might be possible to attach a debugger by running the analysis server with the VM Service enabled (using VS Code's "dart.analyzerVmServicePort" setting, and then the Debug: Attach to Dart Process command). If that would be useful (I suspect so), it's probably worth doing some testing and adding some documentation on this.

@incendial
Copy link

incendial commented Jul 18, 2022

Funny, but creating files worked 😄 . Never tried this before. Okay, I see the problem:

path from analyzeFile: /Users/dimannich/Desktop/code/master-dart-code-metrics/lib/src/analyzer_plugin/analyzer_plugin.dart
context root path: /Users/dimannich/Desktop/code/master-dart-code-metrics
amount of issues: 4

path from analyzeFile: /Users/dimannich/Desktop/code/master-dart-code-metrics/lib/src/analyzer_plugin/analyzer_plugin.dart
context root path: /Users/dimannich/Desktop/code/master-dart-code-metrics/tools/analyzer_plugin
amount of issues: 0

So, it seems to me that it's like one file is being analyzed by 2 contexts and the second one overrides the result of the first analysis. I'd say that behaviour is unexpected and looks like the analysis context from tools/analyzer_plugin should not be used to analyze files not in the tools/analyzer_plugin.

@incendial
Copy link

incendial commented Jul 18, 2022

And I still get the plugin removed by the analysis server after some time... 😞

@scheglov
Copy link
Contributor

Could be dart-lang/sdk#49404

@incendial
Copy link

incendial commented Jul 18, 2022

Could be dart-lang/sdk#49404

Actually, yeah, looks like it, thanks! Any ideas why the plugin might be removed after few minutes? We also faced a problem with the files not being analysed when they are not open, but we're collecting more info on that.

@incendial
Copy link

@DanTup thank you for the help!

@scheglov
Copy link
Contributor

Could be dart-lang/sdk#49404

Actually, yeah, looks like it, thanks! Any ideas why the plugin might be removed after few minutes?

Not really, maybe https://github.com/dart-lang/sdk/blob/f6aaa72e58b0d11d4ae9d84037bbacc17807e7d5/pkg/analysis_server/lib/src/plugin/plugin_manager.dart#L881

Maybe @bwilkerson can remember when else plugins might be unloaded.

Maybe you could hack the plugin (right in the pub cache), and write into a file all notifications that are sent to the server. This way you could see which exception happens.

@incendial
Copy link

After giving the new api to our users we received a report that the problem reported here: #90868 (with multiple plugin instances after pub get) has returned 😞:

1 run: 14.35 GB
2 run: 15.52 GB
3 run: 23.24 GB
4 run: 27.40 GB
5 run: 31.77 GB

@scheglov any chance you can take a look?

@incendial
Copy link

After giving the new api to our users

Also received reports about flickering, now the plugin is not removed, but flickers or after some time just shows no errors.

Another problem that people started to report the issues from the newer package version when they haven't yet updated. Like they have DCM 4.17.0, but IDE somehow reports an issue from 4.18.0. I'm not sure, if it's even possible, but it's already two reports.

@scheglov
Copy link
Contributor

It looks that we have a slightly different problem now.

As you might see below (I had to make it small), there are EvictingFileByteStore._cacheCleanUpFunction isolates stuck running. The reason for this is that EvictingFileByteStore is not supposed to be used by clients, IIRC it has no way to exit.

image

@incendial
Copy link

It looks that we have a slightly different problem now.

As you might see below (I had to make it small), there are EvictingFileByteStore._cacheCleanUpFunction isolates stuck running. The reason for this is that EvictingFileByteStore is not supposed to be used by clients, IIRC it has no way to exit.

image

Oh... Do we need to call the cleanup from the plugin somewhere? We also use EvictingFileByteStore for our commands, do we have the same problem there?

Thank you for looking into that

@JELaVallee
Copy link

Oh... Do we need to call the cleanup from the plugin somewhere? We also use EvictingFileByteStore for our commands, do we have the same problem there?

@incendial just ran into this same issue in a private custom-lint plugin that was also built with EvictingFileByteStore and when it was scanning a project with >40 root packages.

What we were observing: When dart language-server is running either in VSCode or Intellij/Android Studio, everytime we ran flutter pub get we would see ~5GB memory loss. Could easily get to ~30GB mem utilization just switching branches and refreshing pub a couple of times.

We fixed it by changing our MemoryCachingByteStore initialization to use FileByteStore as @scheglov noted:

    return MemoryCachingByteStore(
      FileByteStore(
        stateLocation.path,
        tempNameSuffix: DateTime.now().millisecondsSinceEpoch.toString(),
      ),
      memoryCacheSize,
    );

Now what we're seeing is that initial analysis after opening the IDE still adds about 4-5GB to the memory utilization of the dart language-server proc, but we can see it properly GC'ing the LRU cache in MemoryCachingByteStores when they reach their memoryCacheSize max. 😄

@flutter-triage-bot
Copy link

This issue is marked P1 but has had no recent status updates.

The P1 label indicates high-priority issues that are at the top of the work list. This is the highest priority level a bug can have if it isn't affecting a top-tier customer or breaking the build. Bugs marked P1 are generally actively being worked on unless the assignee is dealing with a P0 bug (or another P1 bug). Issues at this level should be resolved in a matter of months and should have monthly updates on GitHub.

Please consider where this bug really falls in our current priorities, and label it or assign it accordingly. This allows people to have a clearer picture of what work is actually planned. Thanks!

@gspencergoog gspencergoog added team-engine Owned by Engine team and removed team-desktop labels Jul 20, 2023
@a-siva
Copy link
Contributor

a-siva commented Jul 24, 2023

//cc @srawlins

@srawlins
Copy link
Contributor

I believe we can close this. This thread does not represent a single P1 issue. There is not a clear exit state. Much of the changes have been made to one Dart analyzer plugin. We've mitigated issues that arise from multiple Dart analyzer plugins by limiting to one. We're working on a new plugin API. I just don't think there is much to track here.

@a-siva a-siva closed this as completed Jul 24, 2023
@darshankawar darshankawar added the r: solved Issue is closed as solved label Jul 25, 2023
@github-actions
Copy link

github-actions bot commented Aug 8, 2023

This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 8, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
a: desktop Running on desktop c: performance Relates to speed or footprint issues (see "perf:" labels) customer: going dependency: dart Dart team may need to help us engine flutter/engine repository. See also e: labels. found in release: 2.6 Found to occur in 2.6 found in release: 2.8 Found to occur in 2.8 has reproducible steps The issue has been confirmed reproducible and is ready to work on P1 High-priority issues at the top of the work list perf: memory Performance issues related to memory r: solved Issue is closed as solved team-engine Owned by Engine team
Projects
None yet
Development

No branches or pull requests