Skip to content

Instantly share code, notes, and snippets.

@PhongHuynh93
Last active April 20, 2021 04:11
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save PhongHuynh93/08fee1c6e65e4e64b94d835d42b6795a to your computer and use it in GitHub Desktop.
Save PhongHuynh93/08fee1c6e65e4e64b94d835d42b6795a to your computer and use it in GitHub Desktop.
Android Monitor, Performance, Memory
Best Practices for Performance
https://developer.android.com/training/best-performance.html
# Khái niệm stack/heap trong Java
1.Stack: Stack values only exist within the scope of the function they are created in. Once it returns, they are discarded.
Stack memory holds:
. primitives
. method invocations.
. reference to object (because reference is primitive)
2.Heap: They are created at some point in time, and destructed at another (either by GC or manually, depending on the language/runtime).
. Heap memory is used to store objects, with all the variables that belong to it - so that it can persist after the function call returns. (sd new sẽ làm obj nằm trên heap)
# Java Heap Memory Structure:
http://www.javatutorialguide.com/core-java/tutorials/java-memory-management.php
. Young Generation: gồm 3 vùng nhỏ là: Eden and Survivor space 0 và Survivor space 1
. Old Generation: gồm Tenured Space
. Permanent Generation: gồm permGen
# Minor GC vs Major GC vs Full GC
https://plumbr.eu/blog/garbage-collection/minor-gc-vs-major-gc-vs-full-gc
. Minor GC: gọi GC là Minor khi GC xày ra trong vùng "Young Generation"
. Major GC: gọi GC là Major khi GC xảy ra trong vùng "Old Generation"
. Full GC: gọi GC là Full khi GC xảy ra trong vùng "Young + old Generation"
. Khi new object được tạo ra, nó nằm trong vùng "young generation", khi vùng young generation bị đầy thì "minor GC" xảy ra.
=> nếu minor GC xảy ra quá thường xuyên -> tức là quá trình xin bộ nhớ trong heap xảy ra cao (trong 1 khoảng thời gian phải sd new() nhiều)
. Khi minor GC xảy ra, những obj nào còn lai (tức obj đó vẫn còn refer) thì obj đó move đến "old generation" (trong vùng này lưu những obj đã tồn tại lâu rồi) -> tại 1 điểm nào đó nó gọi Major GC (nhưng tần suất lại ít thường xuyên hơn là Minor GC).
=> đừng có nghĩ về GC dc chia thành 3 phần, hãy nghĩ về sự thường xuyên mà GC xảy ra.
# GC (garbage collection): là cái java sẽ tự động làm để lấy lại bộ nhớ mà program xin.
https://www.youtube.com/watch?v=_CruQY55HOk&t=2718s
+ vd: obj thì nằm trong heap, và khai báo new(). Khi ta khai báo new() trong block (vd trong 1 method).
Khi ta đi ra khỏi block (vd ra khỏi method) thì biến mà primitive đến obj bị null (là xóa luôn biến) -> lúc nào obj còn nằm trong heap, GC ko thấy nó dc refer nữa thì sẽ tự xóa obj đó trong 1 khoảng thời gian nào đó trong tương lai.
Nhưng nếu là static thì nó tồn tại trong life của program -> nó cứ giữ obj đó thì GC sẽ ko bao giờ xóa object.
Không thể khai báo static var dạng local trong java, chỉ dc khai báo nó cho instance variable và method thôi.
Sau android Gingerbread, thì GC sẽ chạy concurrent với app(lúc trước thì app phải stop thì GC mới chạy, giờ thì GC chạy trong thread khác), mà App bị PAUSE lúc đầu và lúc cuối khi GC bắt đầu/kết thúc chạy.(cỡ 5ms)
# cấu trúc của GC trong android:
. Lúc đầu GC Roots sẽ trỏ tới 1 obj trong vùng heap mà chắc chắn nó sẽ sống(còn refer)
. sau đó nó di chuyển hết toàn bộ graph(lý do là obj dc trỏ nó lại ref đến 1 obj nào đó trong heap, nó trỏ lẫn nhau trong đây, GC phải traverse hết) -> sau đó những obj mà ko dc GC đi qua sẽ bị xóa
# Managing Your Apps Memory
để GC to reclaim memory from your app, you need to:
. avoid introducing memory leaks (usually caused by holding onto object references in global members)
. release any Reference objects at the appropriate time (as defined by lifecycle callbacks discussed further below).
For most apps, the Dalvik GC takes care of the rest: the system reclaims your memory allocations when the corresponding objects leave the scope of your app active threads.
# Allocating and Reclaiming App Memory
Cách mà Android cung cấp memory cho app của ta:
. The Dalvik heap cho từng process(là 1 app), và mỗi app chỉ có giới hạn ram. (nó sẽ từ từ tăng)
. Cung cấp ram cho từng app trong Android ko giống nhau, và Android phải tính con so PSS để xác định xem app của ta cần bn ram.
. Tuy là logic heap chỉ có tăng ko có giảm(do giống như stack, chỉ giảm ở phần top), còn mấy phần giữa tuy có lây dc memory nhưng android vẫn ko nén heap lại, nhưng mà physical memory vẫn biết là vùng nhớ đó còn trống và ta đã lấy lại physical memory. (biết vùng nhớ còn trống khi GC xảy ra).
# Restricting App Memory
-> đây là con số hard, tức là con số này ko đổi, và Android sẽ cung cấp số này cho mỗi app, ko nhất thiết là giống nhau giữa các app, va số này còn tùy vào bộ nhớ Ram mà device có.
Và khi vung nhớ này đến giới hạn nhưng ta cứ xin (new) thì bị lỗi "OutOfMemoryError" (crash đây).
. Lý do mỗi app đều có 1 hard heap size, do Android là multitask, cùng 1 lúc nhiều app chạy,ta ko thể để cho 1 app nó cứ phình memory ra mà app khác ko sd được.
. Vd về heap size khác nhau trong từng device:
+ G1: 16MB
+ Droid: 24MB
+ Nexus one: 32MB
+ Xoom: 48MB
Do mỗi device thì lượng heap size khác nhau nên nếu ta muốn lấy cái số này thì ActivityManager.getMemoryClass()
. Nhưng lại có 1 số app nó thực sự tốn vùng heap hơn bất cứ app nào khac, vd như app editor bitmap, thì ta sd "largeHeap" trong android manifest
Lưu ý: app phải thực sự cần mới dc lý do, heap lớn thì GC làm lâu, với lại nó chiếm vùng nhớ lớn thì sẽ làm mấy app kia bị destroy, user se biết là app này gây hao tài nguyên nên có nguy cơ xóa.
# Switching Apps
khi process được tạo và ta rồi nó đi (bấm home), thì Android giữ process trong cache LRU (memory mà app chiểm vân giữ lại) -> do ko biến mất nên lúc quay lại thì mở lên nhanh
Nhưng việc giư lại ảnh hưởng performance của toan bộ system -> khi mà bị đầy thì Android sẽ kill process, nhưng ko phải kill theo dạng stack, mà củng cân nhắc giữ lại app mà nó nặng. (vd kill rồi mở lại thì lâu)
# How Your App Should Manage Memory
Để làm 1 app hiệu quả thì phải cân nhắc từ khi design (trước khi code).
1. Use services sparingly
lý do là process có service chạy nó nằm tại rank thứ 3/5 thứ tự mà Android sẽ kill, yêu tiên hơn process bị onStop()
vì Android ít kill nên nó tốn Ram và làm cho các process khác ít bị cache hơn.
2. Release memory when your user interface becomes hidden
Khi user navigates đến app khác -> app của bạn nên release resource -> giúp tăng dung lượng cache -> tăng performance toàn hệ thống.
sd onTrimMemory() callbacks chừng nào UI của mình bị hidden, thay cho onStop() lý do là onStop() dc gọi khi ta nav qua activity khác luôn.
3. Release memory as memory becomes tight
onTrimMemory() callbacks đê biết được dung lượng memory của hệ thống đang bị low -> mặc dù ta vẫn đang trong app nhưng nên release memory
4. Check how much memory you should use
5. Avoid wasting memory with bitmaps
Do là bitmaps nằm trong heap, cho nên độ phân giải của nó càng cao thì chiếm vùng heap càng nhiều
-> phải scale nó sao cho hợp với device.
6. Use optimized data containers
Thay container như "HashMap" bằng 3 cái sau:
Sử dụng khi key là integer
. SparseArray: keys: int values: object
. SparseBooleanArray.
. LongSparseArray
http://stackoverflow.com/questions/25560629/sparsearray-vs-hashmap
7. Be aware of memory overhead
8. Be careful with code abstractions
9. Use nano protobufs for serialized data
10. Avoid dependency injection frameworks
11. Be careful about using external libraries
cái cách sd chỉ 1 vài tính năng của library nhưng sd quyên 1 library
12. Optimize overall performance
https://developer.android.com/training/best-performance.html
13. Use ProGuard to strip out any unneeded code
The ProGuard tool shrinks, optimizes, and obfuscates your code by removing unused code and renaming classes, fields, and methods with semantically obscure names. Using ProGuard can make your code more compact, requiring fewer RAM pages to be mapped
14. Use zipalign on your final APK
15. Analyze your RAM usage
16. Use multiple processes
# Leak memory:
là khi 1 ta ko cần 1 obj mà nó vẫn tồn tại ở đó.
Đây hoàn toàn là phụ thuộc vào cảm nghĩ của ta, vd là ta nghĩ rằng biến này ko dùng nữa thì nó phải dươc free nhưng thực tế nó ko dc free thì gọi là leak
Còn nếu như ta nghĩ rằng biến này sẽ tốn tại hết toàn chtr và nó sẽ ko free thì nó ko dc gõi là memory leak.
Khi ta bị leak memory quá nhiều làm bộ nhớ cho app nhỏ lại -> JVM phải chạy GC liên tục để lấy memory -> gây giảm performance(lag)
. 4 TH bị memory leak in java:
+ Static field holding object reference
class MemorableClass {
static final ArrayList list = new ArrayList(100);
}
Lý do là static sẽ tồn tại trong vòng đời của chương trình (tức chừng nào chtr còn chạy thì biến static vẫn còn).
Cho nên nếu nó refer đến 1 obj thì obj đó ko bị GC lấy đi. (cách là set nó đến null)
=> như vậy nếu như static var refer đến obj, và làm xong obj rồi mà quên set biến static to null thì nó là leak.
=> nhưng nếu TH ta muốn obj tồn tại suốt program thì cứ cho static refer đến nó -> ko bị leak.
Mọi static (instance var + method) nằm trên vùng permgem.
+ Chú ý kiểu khai bao static HashMap và static ArrayList, do mấy cái list thường refer đến obj mà static thì lại tồn tại theo class suốt đời.
+ (Unclosed) open streams ( file , network etc... )
vd sau ta thấy nó đóng InputStream, nếu ko đóng thì bị leak resource (vd như file đang bi đọc thì app khác ko thể đồng thời đọc được file đó, do có thể gặp vấn đề 2 cái cùng access 1 thứ.)
https://gist.github.com/PhongHuynh93/42be08bd07bd06926723ca1f3b0b4484
+ Unclosed connections
+ onclose event listener and callbacks: nếu 1 listener dc register nhưng khi class ko dc sd nữa thì ko unregister nó -> tốn ram
vd:
private void init() {
ListenerCollector collector = new ListenerCollector();
collector.setListener(this, mListener);
}
+ runnable: vd runnable chạy trong vòng 5 phút, nhưng chưa đầy 5 phút ta lại xoay màn hình -> runnable nó vẫn hold đến activity cũ.
Dong nay lam leak: collector.setListener(this, mListener);
when a new activity is created due to the device orientation changing, an associated Listener is created by the view, but when the activity is destroyed, that listener is never released. This means that no listeners can ever be reclaimed by Java's garbage collector, which creates a memory leak.
When the device is rotated and the current activity’s onStop() method is invoked, make sure to clean up any unnecessary references to view listeners.
# Bitmap (ảnh tốn heap rất lớn)
. Trước Honeycomb: nếu bitmap lưu trong heap, nếu những hình đó dc lưu trong tablet thì hình cang to sẽ càng tốn nhiều bộ nhớ nữa.
Cho nên cách lưu bitmap: trong heap chỉ lưu bitmap obj thoi -> mọi obj bitmap đều co same size tùy resolution. obj bitmap này trỏ đến vùng native memory, lưu ảnh bitmap thực sự.
Nhược: phải đợi GC xảy ra mới free được, nếu app nặng về xử lứ image thì ko lẽ đợi GC xảy ra mới chạy được. Nhược thứ 2 là ta ko truy cập dược vùng native memory, nên bitmap lưu trong đây ko debug dc.
. Sau Honeycomb: ảnh bitmap được lưu trong vùng heap luôn, có 1 bitmap obj trỏ tới vung này -> debug được lượng chiếm memory của bitmap.
# Cách xem xét memory:
Xem mục 8
Nhìn trong log (khi GC xảy ra mới dump được memory còn trống bn) hay ta có thể gọi hàm dump ra. Nhưng nó lại ko nói dc obj sd bn memory.
Để thấy dc 1 obj sd heap bn -> sd DDMS, nếu xuất ra 1 file dạng HPROF format thì có thể sd các công cụ Memory Analyze khác (MAT) để phân tích ngoài Android Studio.
# Memory leaks:
GC nó ko đủ thông minh, nó chỉ biết 1 điều: khi dc ref -> nó còn sống, khi ko ref -> dc quyền xóa.
Là khi obj ref đến vùng heap ko sd -> ko cho GC lấy lại memory.
Nhưng khi obj ref đến obj trong vùng heap, và nó lại ref đến một dống obj khác -> lam cho GC ko thể nào lấy lại dc memory.
vd ta có cây ref sau:
activity -> viewgroup -> nhiều view nhỏ => nếu 1 obj giữ 1 activity ko sd thì ko biết bộ nhớ leak bn.
=> Khi config xoay màn hình, ko dc ref đến activity cũ.
=> muốn xem dc mình có vô tình lảm leak ko => sd công cụ "Memory Analyze".
. Inner class: là class được dn trong 1 class khác
. Non static inner class: dc quyền access member của class ngoài ->luôn phải giữ implicit với class ngoài -> nếu 1 obj ref đến class trong, mặc dù class ngoài ko cần nữa nhưng do class trong ref đến
class ngoài nên obj class ngoài ko bị GC xóa.
. Static inner class: ko cần tạo instance của class ngoài vẫn access dc class trong, class trong có thể ko dc quyền access member của class ngoài, ko giữ refer đến class ngoài.
=> chỉ cần 1 chữ static đã đỡ tốn memory biết bao nhiêu.
# Activity và View:
Activity luôn có implicit ref đến các Views obj con.
View cũng có ref đến Activity nữa -> lý do khi tạo View nó phải biết tạo ở đâu, nó có chứa những view con ko (2 hàm getContext(), và getChildAt()).
View chứa bitmap thì nó ref đến Bitmap đó.
=> Nếu bạn ref đến 1 Activity (hay Activity context), bạn đã có thể tham chiếu tới toàn bộ cây gồm các view nữa -> leaks thì tốn rất nhiều đây.
-> Cách tránh:
. Tránh tạo static ref đến View hay Activity (Activity Context)
. Nếu mà ref đến context thì nên short lived (đặt trong 1 hàm)
. Nếu như cần long live context, sd Application Context (getBaseContext() or getApplicationContext()). These do not keep references implicitly.
# runnables leak memory: http://stackoverflow.com/questions/10864853/when-exactly-is-it-leak-safe-to-use-anonymous-inner-classes
# Memory Analyze (MAT trong eclipse)
. Shallow heap: chỉ size của từng object trong heap
. Retained heap: chỉ size khi ta free 1 obj thì ta lấy lại dc bn memory (lý do là obj nếu trỏ tới obj khác thì các obj đó củng được free).
Trong công cụ này có một thuật ngữ là "Dominator Tree": A là dominator của B khi path qua B phải qua A. -> nhờ cái cây này mà ta biết được retained heap.
# Debug memory
Khi memory đi lên nhưng ko đi xuống -> bị memory leaks (ta force GC xảy ra xem nó có làm xuống ko).
# Android Monitor Overview
https://developer.android.com/studio/profile/android-monitor.html#logcat
Dùng để do đạc performance của 1 app: để ta có thể optimize, debug, improve nó bằng 3 cách:
. Log messages, either system- or user-defined: dùng để debug trong realtime.
. Memory, CPU, and GPU, Network usage (2)
. Network traffic (hardware device only)
-> Bạn có thể capture data khi bạn chạy app và xuất ra 1 file, chụp hình, quay phim.
Trong (2) ta có công cụ Memory Monitor:
Android Monitor Basics
# https://developer.android.com/studio/profile/am-basics.html
Các bước:
1. chạy app
2. chọn Memory Monitor
# Memory Monitor
https://developer.android.com/studio/profile/am-memory.html
https://www.youtube.com/watch?v=7ls28uGMBEs
Android Monitor provides a Memory Monitor so you can more easily monitor app performance and memory usage to find deallocated objects, locate memory leaks, and track the amount of memory the connected device is using.
Nhìn màn hình memory:
. màu xanh đậm ở dưới: memory mà app đang sử dụng.
. màu xanh nhạt ở trên: memory còn trống mà app có thể sử dụng.
Các loại group:
TH1: Nếu như app ko làm gì nhiều -> thấy 2 màu xanh là 1 đường thẳng. Đây là TH ideal performance.
TH2: đường thẳng bấp bên thì lúc lên thì app đang xin memory và lúc xuống thì app đang free memory(garbage collection events - GC xảy ra).
+ Khi GC xảy ra thì nó ko hẳn là ảnh hưởng performance lắm, nhưng nếu GC xảy ra nhiều lần trong 1 khoảng thời gian ngắn -> performance issues.
+ Khi GC xảy ra nhiều lần, thì thời gian làm những công việc khác ít dần như render màn hình (gây ra lag) hay stream audio.
+ Khi GC xảy ra nhiều lần, dump the Java heap to identify candidate object types that get or stay allocated unexpectedly or unnecessarily.
-> nhìn cái này ta sẽ kết luận được độ heal và performance của systems. -> nhưng ta ko thể biết problem nào mà liên quan đến nó cả.
=> sd công cụ khác là Memory Heap tool, chổ nào trong code, objects được tạo ra mà ko có release, object nào được create mà ko xài,
hay object nào được new(tạo mới) trong khi ta hoàn toàn có thể sử dụng lại object đó.
# Allocation tracking records
https://developer.android.com/studio/profile/allocation-tracker-walkthru.html
Allocation tracking records app memory allocations and lists all allocations for the profiling cycle, including the call stack, size, and allocating code. It helps you to:
Identify where many similar object types, from roughly the same call stack, are allocated and deallocated over a very short period of time.
Find the places in your code that may contribute to inefficient memory use.
The Allocation Tracker is useful when you want to get a sense of what kinds of allocation are happening over a given time period,
but it doesn't give you any information about the overall state of your application's heap.
// HPROF Viewer and Analyzer
https://developer.android.com/studio/profile/heap-viewer-walkthru.html#WhatYouNeed
// HPROF Viewer -> take a snapshot
https://developer.android.com/studio/profile/am-hprof.html
# HPROF Viewer -> take a snapshot của heap thôi
When you dump the Java heap, the Memory Monitor creates an Android-specific Heap/CPU Profiling (HPROF) file that you can view in the HPROF Viewer.
The HPROF Viewer displays classes, instances of each class, and a reference tree to help you track memory usage and find memory leaks.
The HPROF Analyzer finds the following potential issues:
. All destroyed activity instances that are reachable from garbage collection roots.
. Where the target program has strings that repeat values.
A dominator is at the top of a tree. If you remove it, you also remove the branches of the tree it dominates, so it’s a potential way to free memory.
Understanding the HPROF Viewer Display
https://developer.android.com/studio/profile/am-hprof.html#display
If you click Analyzer Tasks, the HPROF Analyzer appears:
-> You can detect leaked activities and find duplicate strings with the HPROF Analyzer.
# Analyzing Heap Dump Data in the HPROF Analyzer
Bật task này ở phía bên phải, có 2 tùy chọn là
. Duplicated string.
. Detect Leaked Activities -> nếu ko có thì ko leak
vd nếu có leak memory:
http://tools.android.com/recent/androidstudio15preview1available
# Memory leak and use analysis
1. Another area that deserves attention is objects that the app no longer needs but continues to reference.
You can gather heap dumps over different periods of time and compare them to determine if you have a growing memory leak, such as an object type that your code creates multiple times but doesn’t destroy.
2. Continually growing object trees that contain root or dominator objects(obj cha dominator vẫn refer đến con mặc dù con ko sd nữa) can prevent subordinate objects (obj ở dưới cây dominator) from being garbage-collected.
-> This issue is a common cause of memory leaks, out-of-memory errors, and crashes.
# Diving into Heap Dump Data in the HPROF Viewer
The following steps outline the typical workflow:
. In the HPROF Viewer, select a class name.
. Select an instance of that class.
. Examine the reference tree.
. Right-click an item to Jump to source or Go to instance, as needed.
# Android Performance Patterns: Performance Cost of Memory Leaks
https://www.youtube.com/watch?v=h7qHsk1nWKI&list=PLWz5rJ2EKKc9CBxr3BVjPTPoDPLdPIFCE&index=72
Investigating Your RAM Usage
là cách xác định xem lượng bộ nhớ RAM mà chtr sử dụng.
https://developer.android.com/studio/profile/investigate-ram.html
0. sd memory monitor: view state of memory over time
Nhìn tổng quan xem GC có xảy ra liên tục trong 1 khoảng thời gian ngắn hay ko
1. sd heap viewer: what on the heap
# Heap view trong DDMS: xem real time độ tăng giảm memory -> thích hợp dò xem class activity nào bị leak
ta sẽ chuyển từ activity dang chạy 2 -> activity blank 1 (chuyền bằng nút Back)
trong qua trình chuyển -> force GC
# Heap dump: thích hợp xem cái nào refer đến activity đó
Activity 2 sd heap tool để do heap dump activity lúc này, sau đó khi chuyển sang 1 đo lại heap dump.
Khi chuyển qua thì activity cũ phải dc clean up -> remove toàn bộ activity
Khi sd Heap dump thì có khoảng 2000 objs lận, ta chỉ dò Activity xem có instance ko
-> thường thì chỉ có 1 instance của activity thôi
-> nếu ta xoay màn hình, thì trong 1 lúc ngắn thì có 2 instance của activity.
=> thây 2 cái thì nghi là bị leak rồi.
-> khi thấy leak, click vào activity xem cái nào refer đến activity đó (cái refer nghi ngờ nhất là có icon có 3 mũi tên chọt 3 hướng)
2. sd allocation tracker để xem leak cho rõ hơn: where did it come from
tạo 1 blank ac (start allocation) -> activity chính -> (force GC) -> (bấm nút back đi) blank ac (stop allocation)
Nó sẽ liệt kê:
. list các obj đã create.
. thứ tự create.
. create ở đâu.
Mở tab Memory ra -> chọn start Allocation Tracking -> sử dụng activity chính (lướt lướt nó để obj dc tạo ra)
Sd cái này đẻ xem tại sao object ko dc remove khi bấm back
Xác định memory churn: là qua trình tao ra rất nhiều object trong 1 khoang thời gian ngắn, vd:
. tạo ra nhiều object ở trong vòng lặp for
. hay tạo ra nhiều object on hàm ondraw() function -> vd khi animation xẽ goi ondraw trong mỗi frame (khi nhấn button thì có
hiệu ưng trong button) -> cùng 1 thời gian ngắn mà xin heap 1 lg lớn -> heap ko đủ cc 1 lg lớn đó thì nó goi GC
=> giai pháp: remove khỏi inner loop (tránh loop nhiều lần), remove khỏi hàm nào dc gọi liên tục nhiều lần.
=> ta sẽ sd cong cụ trace view để tìm memory churn.
3. sd traceview
https://developer.android.com/studio/profile/traceview-walkthru.html
How to fix app quality issues with Android vitals (Part 1)
https://medium.com/googleplaydev/how-to-fix-app-quality-issues-with-android-vitals-and-improve-performance-on-the-play-store-part-498dde9f4ef6
conducted last year, we looked at one-star reviews on the Play Store and found over 40% mentioned app stability as an issue. Conversely, people consistently reward the best performing apps with better ratings and reviews.
In this article, I’m going to look at two of these issues:
Excessive wakeups. These impact battery life
Application Not Responding (ANR) events.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment