preface

TT voice will create about 240 threads every time it starts the app and enters the home page, nearly half of which are threads created by obtaining the SharePreferences object. Many of these threads are one-time and are not destroyed immediately after use, which will occupy memory. Moreover, frequent threads are created. May lead to a number of threads OOM causes too much, therefore, needs to find space, can be optimized as much as possible and then optimize these threads, but, if you want to find the optimization space, you need to understand the whole thread of app usage, listening to which page, which lines of code, which creates a thread, to bring to our optimization direction with accuracy

Monitoring plan

Through AOP runtime staking, here with the help of Epic, listen to the creation of all threads in APP, can listen to which page, which line of code, create that thread, and then through the log to print the creation of thread information, the code is as follows

class ThreadCheck : XC_MethodHook() { var isCanAppendLog = false override fun afterHookedMethod(param: MethodHookParam?) { super.afterHookedMethod(param) if (param? .thisObject is Thread) { val thread = param.thisObject as Thread val es = Thread.currentThread().stackTrace val normalInfo = StringBuilder(" \nThreadTrace:") .append("\nthreadName:${thread.name}") .append("\n====================================threadTraceStart=======================================") for (e in es) {  if (e.className == "dalvik.system.VMStack" && e.methodName == "getThreadStackTrace") { isCanAppendLog = false } if (e.className == "me.weishu.epic.art.entry.Entry" && e.methodName == "referenceBridge") { isCanAppendLog = true } else { if (isCanAppendLog) { normalInfo.append("\n${e.className}(methodName:${e.methodName},lineNumber:${e.lineNumber})") } } }  normalInfo.append("\n=====================================threadTraceEnd=======================================") Log.i(tag, normalInfo.toString()) } } companion object { const val tag = "====>ThreadCheck" fun hook() { if (! BuildConfig.DEBUG) { return } DexposedBridge.hookAllConstructors(Thread::class.java, ThreadCheck()) } } }Copy the code

Log Print format

It can be seen from the above figure and the following figure that the class below created AsyncTask and did not destroy it immediately after use, resulting in a waste of memory. In addition, frequent thread creation may cause the APP to become OOM due to too many threads

Analysis of the

As shown in the figure above, the TT voice appears in a large number of similar OOM models on android8.0 or above, which is caused by too many threads or insufficient virtual memory. Creating threads requires virtual memory, and different models allow different maximum threads. For example, on some huawei models, The cap of the modified is very low (about 500), it is important to note that the APP work = total number of threads in the thread + sleep state the number of threads, so these phones prone to overflow problem, the number of threads and TT voice entered the home page will probably create about 240 threads, although there are a lot of threads are soon the end of the work, Then become useless threads, meaning that these threads after work are not accounting into the total number of threads of the APP, however, often create a thread is still may lead to OOM in the image above, because although they work time is very short, but still may be the last straw breaks the camel, and often create a thread, can cause the consumption of virtual memory, Increase the possibility of OOM. Therefore, minimizing the creation of threads is a key step of thread optimization. Then, start counting thread usage, as follows:

Thread usage

1. A Thread created using a new Thread or new AsyncTask or new HandlerThread, as shown in the figure above. The thread that was created when the SharePreferences object was acquired and will be re-created each time the SharePreferences object is acquired because the mode in which we obtain the SharePreferences object is multi-process mode. In this case, a new thread is created each time the SharePreferences object is retrieved. The thread created by the coroutine in the code 4. The thread created by the third-party SDK and the thread created by the SharePreference object 5. Threads created using thread pools in code 6. Threads created using Maven librariesCopy the code

Optimize the way

  1. When retrieving the SharePreferences object, consider whether the data is used in multiple processes. If not, change the mode to MODE_PRIVATE so that the thread is not recreated every time you retrieve the SharePreferences object
  2. For threads created in code using new Threads or new AsyncTask or new HandlerThread, it is recommended to reuse threads using Thread pools or coroutines instead

QQ communication group of

770892444