27 October 2022
Posted by Niharika Arora, Developer Relations Engineer
In Part 1 and Part 2 of our “Optimizing for Android Go” blog series, we discussed why we should consider building for Android Go and how to optimize your app to perform well on Go devices. In this blog, we will talk about the tools which helped Google optimize their Google apps performance.
PSS is useful for the operating system when it wants to know how much memory is used by all processes since pages don’t get counted multiple times. PSS takes a long time to calculate because the system needs to determine which pages are shared and by how many processes. RSS doesn't distinguish between shared and non-shared pages (making it faster to calculate) and is better for tracking changes in memory allocation.
2. Take a heap dump and analyze how much memory is utilized by the running processes. FollowSo, which method should you choose? The choice depends on the usage of shared memory.
For example, if the shared memory is being used by the application only then we should use the RSS approach. While, if the shared memory is taken by the Google Play Services then we should use the USS approach. For more understanding, please read here.
adb logcat | grep hprof
adb pull <output-file-name>
This will pull the generated file to your machine for analysis.
To get info on native heap, read here :
To know about Java heap, read here :
3. Understand low-memory killer
In Android, we have a process called low memory killer, and this will pick a process from the device and will kill that process when the device is under low RAM, the thresholds can be tuned by OEMs. By doing so, you will get back all the memory that the process was using.
But what if the low memory killer kills the process that the user cares about?In Android, we have a priority list of applications and based on that priority list we remove the app when the low memory killer comes into play. Read more here.You can run this command and know :adb shell dumpsys activity oomTo check stats on low memory killer :adb shell dumpsys activity lmkFor more information, please check Perfetto documentation for Memory.
The Memory Profiler is a component in the Android Profiler that helps you identify memory leaks and memory churn that can lead to stutter, freezes, and even app crashes. It shows a real time graph of your app's memory use and lets you capture a heap dump, force garbage collections, and track memory allocations. To learn more about inspecting performance, please check MAD skills videos here.
You may want to observe how your app's memory is divided between different types of RAM allocation with the following adb command:
adb shell dumpsys meminfo <package_name|pid> [-d]
You can view the following seven memory categories with Meminfo:
Key memory terms:
Note :
The showmap command provides a much more detailed breakdown of memory than friendly meminfo. It lists the name and sizes of memory maps used by a process. This is a summary of the information available at /proc/<pid>/smaps, which is the primary source of information used in dumpsys meminfo, except for some graphics memory.
$adb root
$ adb shell pgrep <process>
Output - process id
$ adb shell showmap <process id>
Sample Output :
Common memory mappings are:
Malloc debug is a method of debugging native memory problems. It can help detect memory corruption, memory leaks, and use after free issues. You can check this documentation for more understanding and usage.
Beginning with Android 27, Android NDK supports Address Sanitizer which is a fast compiler-based tool for detecting memory bugs in native code. ASan detects:
ASan runs on both 32-bit and 64-bit ARM, plus x86 and x86-64. ASan's CPU overhead is roughly 2x, code size overhead is between 50% and 2x, and the memory overhead is large (dependent on your allocation patterns, but on the order of 2x). To learn more, read here.
Camera from the Google team used it and automated the process that would run and get back to them in the form of alerts in case of Asan issues, and found it really convenient to fix memory issues missed during code authoring/review.
Once you have a complete app startup trace, look at the trace and measure time taken for major operations like bindApplication, activitystart etc.Look at overall time spent to
This is your app’s home page and often performance will depend on the loading of this page. For most apps, there is a lot of data displayed on this page, spanning multiple layouts and processes running in background. Check the home activity layout and specifically look at the Choreographer.onDraw method of the home activity.
The App Startup library provides a straightforward, performant way to initialize components at application startup. Both library developers and app developers can use App Startup to streamline startup sequences and explicitly set the order of initialization. Instead of defining separate content providers for each component you need to initialize, App Startup allows you to define component initializers that share a single content provider. This can significantly improve app startup time. To find how to use it in your app, refer here.
Baseline Profiles are a list of classes and methods included in an APK used by Android Runtime (ART) during installation to pre-compile critical paths to machine code. This is a form of profile guided optimization (PGO) that lets apps optimize startup, reduce jank, and improve performance for end users. Profile rules are compiled into a binary form in the APK, in assets/dexopt/baseline.prof.
During installation, ART performs Ahead-of-time (AOT) compilation of methods in the profile, resulting in those methods executing faster. If the profile contains methods used in app launch or during frame rendering, the user experiences faster launch times and/or reduced jank. For more information on usage and advantages, refer here.
You can use the CPU Profiler to inspect your app’s CPU usage and thread activity in real time while interacting with your app, or you can inspect the details in recorded method traces, function traces, and system traces. The detailed information that the CPU Profiler records and shows is determined by which recording configuration you choose:
To give apps the ability to start and stop recording CPU profiling and then inspect in CPU profiler is what Debug API is all about. It provides information about tracing and allocation counts the same way using startMethodTracing() and stopMethodTracing().
Debug.startMethodTracing("sample") - Starts recording a trace log with the name you provide Debug.stopMethodTracing() - he system begins buffering the generated trace data, until the application calls this method. |
Usage
This sample can be used for app instrumentation.
<profileable android:shell=["true" | "false"] android:enable=["true" | "false"] />
The Jetpack Microbenchmark library allows you to quickly benchmark your Android native code (Kotlin or Java) from within Android Studio. The library handles warmup, measures your code performance and allocation counts, and outputs benchmarking results to both the Android Studio console and a JSON file with more detail. Read more here.
No user wants to download a large APK that might consume most of their Network/Wifi Bandwidth, also most importantly, space inside the mobile device.
The size of your APK has an impact on how fast your app loads, how much memory it uses, and how much power it consumes. Reducing your app's download size enables more users to download your app.
The Android Size Analyzer tool is an easy way to identify and implement many strategies for reducing the size of your app. It is available as both an Android Studio plugin as well as a standalone JAR
The lint tool, a static code analyzer included in Android Studio, detects resources in your res/ folder that your code doesn't reference. When the lint tool discovers a potentially unused resource in your project, it prints a message like the following example.
Note : Libraries that you add to your code may include unused resources. Gradle can automatically remove resources on your behalf if you enable shrinkResources in your app's build.gradle file.
In Android 12 (API level 31), the NDK ImageDecoder API has been expanded to decode all frames and timing data from images that use the animated GIF and animated WebP file formats. When it was introduced in Android 11, this API decoded only the first image from animations in these formats.
Use ImageDecoder instead of third-party libraries to further decrease APK size and benefit from future updates related to security and performance.
For more details on the API, refer to the API reference and the sample on GitHub.
The aapt tool can optimize the image resources placed in res/drawable/ with lossless compression during the build process. For example, the aapt tool can convert a true-color PNG that does not require more than 256 colors to an 8-bit PNG with a color palette. Doing so results in an image of equal quality but a smaller memory footprint. Read more here.
Note : Please check Android developer documentation for all the useful tools which can help you identify and help fix such performance issues.
This part of the blog captures the tools used by Google to identify and fix performance issues in their apps. They saw great improvements in their metrics. Most Android Go apps could benefit from applying the strategies described above. Optimize and make your app delightful and fast for your users!