/ Memory partition /

Crank is an ADB root command that can query memory allocations:

  • VSS – Virtual Set Size Virtual memory (including shared library memory)
  • Rss-resident Set Size Actual physical memory used (including memory occupied by all shared libraries)
  • Pss-proportional Set Size Actual physical memory (Proportional allocation of shared library memory)
  • Uss-unique Set Size Physical memory occupied by the process alone (excluding memory occupied by the shared library)

The most notable ones are PSS and USS, which can be queried using Dumpsys meminfo (no root permissions required).

Dumpsys meminfo queries PSS partitioning

Key field interpretation:

  • Native Heap – Native Heap memory
  • Dalvik heap-Dalvik VM memory, Dalvik VM code in libvm. So is responsible for the runtime dex parsing into machine code (Android 5.0+ ART has cancelled Dalvik VM, Dalvik still appears here.
  • Android_runtime. so, mmap (is a function interface for Linux C, used to do memory mapping)
  • Private Dirty – Memory that is exclusively used by a process. Memory that has been modified by this process can only be used by its own process
  • Private clean – Memory that is exclusive to the process. Memory is mapped, not modified, and can be replaced for use by other processes
  • Java heap – Dalvik heap (dirty+ clean) + art heap (dirty+ clean)

According to the picture above, the memory used by launcher app is 250M, and most of it contains Native Heap, code and graphics. How to analyze and solve this problem will be discussed below.

The Android Studio Profile is a taxonomy provided by the IDE:

  • Total – Total memory occupied by the entire application
  • Java – The memory occupied by the Java heap
  • The memory occupied by the native-native layer calling malloc/new (C/C++)
  • Display pixels to the screen (ignore them if you’re not using OpenGL or not a game)
  • Stack – The amount of memory occupied by the thread Stack
  • Memory occupied by code-dex +so library
  • Others – Unsolved mysteries

The JMM classification

  • Method area – an area where constants and static variables are stored
  • This is where the memory of objects that come out of Java heap-new is stored
  • Java stack – Primitive type variables and variable references executed in methods are on the stack. (References to internal member attributes in a class are in the heap.)
  • Native stack – Like Java stack, pointer references are in native stack
  • Program counter – The function of multithreading to switch records of the last thread to the point of execution. For example, thread A switches to thread B. The program counter records which line of code thread A has executed. Then the CPU switches to thread B, and when it switches back to thread A, the CPU knows which line of code to continue from thread A

Note:

  • The Java stack, Native stack, and program counter are thread private
  • The Java heap and method area are common to threads

/ Java memory optimization /

Java Memory optimization A memory leak Memory jitter Large memory object usage
Scene of occurrence Singletons, anonymous inner classes, interfaces forget to release… String concatenation, repeated generation of objects within loops… A HashMap, ArrayList…
Java check leak – LeakCanary used
LeakCanary Result analysis

LeakCanary Can check for leaks in the Activity Fragment View. Run to monkey by plugging LeakCanary and wait for a Java memory leak to occur:

By above may know SearchActivity HistorySource. MContext hold, HistorySource is a singleton, and then the top Thread. ContextClassLoader is GC root (note: Static variables not GC root), Thread. ContextClassLoader is PathClassLoader class, as long as the SearchActivity context for the Application that is solved.

Android GC root
  • Classes that are loaded by the System ClassLoader and then generated, such as the classes in rt.jar
  • PathClassLoader, DexClassLoader
  • Thread alive Thread
  • Local variables in function methods (running in threads)
  • Global and local variables in JNI
The core principle behind LeakCanary:
  • Through registerActivityLifecycleCallbacks () to monitor the Activity of exit
  • After the Activity exits, the Activity object is encapsulated into a KeyedWeakReference weak reference object.
  • By manual runtime.getruntime ().gc(); The garbage collection
  • Through removeWeaklyReachableReferences () manually removed has been recovered
  • The gone() function is used to determine if the Activity is removed. If it is removed, no other strong references are being made to it and no leaks are being made
  • If not, use the Android native interface debug.dumphprofData () to pull down the Hprof file and use the haha third-party library to resolve whether there are any residues of the specified Activity. (Haha is the Java library for analyzing Hprof)
summary
LeakCanary can only fix leaks on the interface, not other memory optimizations, such as thread pool leaks, memory jitter, large object abuse… A more powerful tool, MAT, is needed.
Memory detection tool MAT
MAT is a tool for analyzing memory files hprof.
Grab steps
After running the Monkey for a few minutes, return to the app’s home screen and manually click the GC button several times to recycle the recyclable data in order to eliminate dirty data. Dump the hprof file from the Android Studio Profile.

  • Go to the AndroidSDK directory: G:\AndroidSDK\platform-tools
  • Dump file memory-20190828t162317.hprof into platform-tools
  • Type CMD hprof-conv memory-20190828t162317.hprof 1.hprof to a 1.hprof file that can be recognized by MAT
  • Use MAT to open 1.hprof

Analyze the resulting graph of memory after completing the above steps.

  • Click directly on Histogram in the upper-left corner to see the memory distribution
  • Objects – Number of objects
  • Shallow heap – The actual heap size occupied by the object itself
  • Retained heap – How much memory can be freed after the object is recycled
  • Inspector – You can see who the GC Root of an object is

  • With outgoing References – Indicates the current object and which internal member objects are referenced
  • With incoming references – Indicates which external objects are applied to the current object.

  • Merge Shortest Paths to GC Roots – Common paths from GC roots to one or a group of objects
  • Exclude all phantom/weak/soft etc. References – Exclude some types of references (soft, weak, virtual) and leave strong references

In order to avoid viewing too many objects that are not strongly related, MAT provides regular filtering directly from the Java class of the application, and directly input.com.vd. (packageName) to filter. The result is very obvious, and the memory occupied by the whole application’s own objects is here. Start with a large object, whether the object has a purpose to exist, whether it needs to occupy such a large amount of memory. Whether it can be processed accordingly.

MAT provides more convenient OQL queries to find objects with a given name, including the ability to make conditional statements based on member attributes of their Own Java objects. For example, in the image above, I’m looking for images that are larger than 100px in length and width. We can pull out the big picture.

summary
LeakCanary can be used to leak an obvious memory leak, and MAT can be used to check the application’s memory distribution to optimize the optimized Java heap memory.

/ Native memory optimization /

Native memory optimization malloc_debug heapsnap DDMS
Root access Need to be Need to be Don’t need
The environment python jni Need to use sdK18 tools/ddms.bat(removed after SDK 18)
  • Malloc_debug is one of the official recommended methods, and it works well so far

  • Heapsnap is a github open source library that can run on Adnroid, currently can only query memory leaks. And compile however, the reason is missing some libraries. On the basis of it I integrated a compilation of success, interested click here

  • DDMS is currently abandoned, not successful in Android 9.0, abandoned.

Malloc_debug Step Enable the malloc debug mode, open the CMD window, and enter.

Adb shell setprop wrap.packagename'"LIBC_DEBUG_MALLOC_OPTIONS=backtrace logwrapper"'Adb shell setprop wrap.packagename'"LIBC_DEBUG_MALLOC_OPTIONS=leak_track logwrapper"'
Copy the code

  • Close the app, open it again, and the Monkey runs
  • By the adb shell dumpsys meminfo com. All. Videodownloader. Videodownload check the pid for 2968

Adb shell am dumpheap -n <PID_TO_DUMP> /data/local/ TMP /heap. TXT

Copy the native memory file and analyze it later.

Analysis using Python
Set up the environment
  • Download native_heapdump_viewer. Py
  • For the Python compiler I chose PyCharm
  • Create a new project and place native_heapdump_viewer.py and heap.txt in the same directory as shown below

Native_heapdump_viewer.py native_heapdump_viewer.py

resByte = subprocess.check_output(["G: / AndroidNDK/android - the NDK - r17 / toolchains/aarch64 - Linux - android 4.9 / prebuilt/Windows - x86_64 / bin/aarch64 - Linux - android - ob jdump"."-w"."-j".".text"."-h", sofile])
Copy the code
p = subprocess.Popen(["G: / AndroidNDK/android - the NDK - r17 / toolchains/aarch64 - Linux - android 4.9 / prebuilt/Windows - x86_64 / bin/aarch64 - Linux - android - AD dr2line"."-C"."-j".".text"."-e", sofile, "-f"], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
Copy the code

Replace def init(self) with the following:

iflen(extra_args) ! = 1:print(self._usage)
      sys.exit(1)
Copy the code

Replace with:

self.symboldir = "C:/Users/chaojiong.zhang/Documents/AndroidStudio/DeviceExplorer/xiaomi-mi_8-4b429b4"
extra_args.append("dump.txt")
Copy the code

The inside of the self. Symboldir – is a dump. TXT memory address all need through so library which is to find the corresponding function. Self.symboldir = /system/lib64 /vendor/lib64/ self.symboldir = /system/lib64 / Such as this is a pull to the C: / Users/chaojiong. Zhang/Documents/AndroidStudio/DeviceExplorer b429b4 / xiaomi – mi_8-4.

In def main(), the following code is inserted on the first and last lines of the function. The purpose is to output the result log directly to test.txt for direct viewing.

def main(): sys.stdout= open("test.txt"."w") / /... sys.stdout.close()Copy the code

Run and see.

Malloc_debug Memory file analysis
Field interpretation
  • BYTES- Specifies the size of the memory used in BYTES
  • %TOTAL – Indicates the percentage of TOTAL Native memory
  • %PARENT – Percentage of PARENT frame memory
  • COUNT – How many times is called
  • ADDR- Memory address
  • Library-which so LIBRARY the occupied memory belongs to
  • FUNCTION- Which method the occupied memory belongs to
  • LOCATION – Which row the occupied memory belongs to
Memory information analysis 1
10285756 58.29% 99.95% 49 eac0b276 / system/lib/libhwui. So android: : Bitmap: : allocateHeapBitmap (SkBitmap *)Copy the code
It can be seen that the allocateHeapBitmap method takes up about 10 MB of memory, accounting for 58.29% of the total native memory and 99.95% of the parent frame. %PARENT = %PARENT; %PARENT = %PARENT; %PARENT = %PARENT; %PARENT = %PARENT; %PARENT = %PARENT; %PARENT = %PARENT; %PARENT = %PARENT Action in libhwui. So the android: : Bitmap: : allocateHeapBitmap method. Here is the process where allocateHeapBitmap is called:
BitmapFactory. DecodeResource - > BitmapFactory. NativeDecodeStream - > BitmapFactory. CPP in nativeDecodeStream () - >doDecode() -> SkBitmap.tryAllocPixels() -> ... -> android::Bitmap::allocateHeapBitmap()
Copy the code
Bitmap.createbitmap -> nativeCreate() -> Bitmap.cpp nativeCreate() -> Biticsjni.cpp zhong de allocateJavaPixelRef() - >... -> android::Bitmap::allocateHeapBitmap()Copy the code

This means that all bitmap creation in the Java layer goes to the allocateHeapBitmap function. So the above allocateHeapBitmap, which takes up 10M, is called by which class of Java layer. There is no solution to this at present (including DevEco, huawei’s ark environment platform recently), so we can only query the whole picture in The Java layer, which pictures use more memory. Memory information analysis ii

  • Webviewgoogle. apk is the WebView used by the app. Prior to Android 5.0, it existed as a module in the frameworks/base directory and provided an interface. Android 5.0 is now compiled as a standalone APK with the package name com.android.webView. Check all WebView usage, such as whether webView.clearCache () is called after use if the scenario allows.
  • Boot-framework. oat accounts for 5M. Android Framework code is converted into OAT binary files (machine code) through Dex2OAT without optimization
  • Libandroid_runtime. so occupies 5M of vm memory and is a shared library proportionally. No optimization is required
summary
At present, native memory cannot clearly locate the corresponding Java layer code, there is no solution. You can only look at the general, and then purposefully look at a class, or a module.

/ graphics memory optimization /

If your application does not have its own access to the OpenGL/ GL Surfaces/GL Textures open source library to draw graphics, don’t bother. After all, it’s beyond the scope of Android application engineers.

/ stack memory optimization /

Resolving stack overflow
Dead-loop problem
HaskMap prior to JDK 1.8, avoids the problem of using multiple threads to create an infinite loop.
Recursive problem
Avoid deep recursion problem, deep recursion can use tail recursion method. Recursive exit, preferably with identifier bit exit. Or interrupt(), isInterrupted() to exit the recursion, ensuring that the recursion exits correctly. If thread. sleep is present in the recursion, watch out for interrupt consumption.
Internet problem
For intents passing large objects, or arrayLists, the upper limit for intents is 505K. Solution:
  • Static holds objects that need to be passed.
  • Redirect pages as fragments, data can be retrieved without passing
  • Via EventBus RxBus (the principle is all passed through the global singleton)
  • The object is converted to a JSON string using ObjectCache, which is stored locally and serialized as an object when retrieved.
Resolve repeated generation of local variables
Avoid repeating local variables within the loop
 private void memoryShake() {
        ArrayList<Integer> shakes = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            Integer shake = new Integer(i);
            shakes.add(shake);
        }
    }

    private void memoryShake1() {
        ArrayList<Integer> shakes = new ArrayList<>();
        Integer shake;
        for(int i = 0; i < 100; i++) { shake = new Integer(i); shakes.add(shake); }}Copy the code

MemoryShake () will generate 100 Shake local variables +100 references to local variables within the loop, and memoryShake1() will generate 100 Shake local variables +1 reference to local variables within the loop, with an object reference of 8 bytes in 64bit environments. 100 x 8 = 800 bytes = 0.8KB.

String usage problems

Do not use the + symbol for concatenation of characters in the loop (using the + symbol, when compiled into bytecode, the Loop generates a StringBuilder object to concatenate). The correct use should be StringBuffer (thread-safe) or StringBuilder (thread-unsafe).

/ code memory optimization /

Code memory consumption is mainly: SO library, dex, TTF.

All three files have to be loaded into runtime memory to be parsed, so their volume is counted in their own application memory. Add LOCAL_STRIP_MODULE:= true to android. mk.

Dex is a bytecode compiled from Java code. The dex in apK without obtruding is much larger, and the dex after obtruding is much smaller. Therefore, the memory usage of debug package is larger than that of release package. Android Studio 3.3 comes with a new feature called R8 compression, which adds Android.enabler8 =true to gradle.properties to reduce the size of dex packages (perfectly compatible with existing obversions). Android Studio Menu > Refactor > Remove Unused Resources

TTF – If only part of the font is used in the application, the font used can be extracted by FontZip.