結論
1.創建一個線程,并不是會直接增加1M內存,如果創建的是一個不退出的空線程,在華為P20pro、p40pro手機上,大致32Kb左右;
2.網上說1M的文章,大部分是使用Runtime API獲取內存大小,一般是因為創建了對象 或者做了其他才會有這么大;
3.死循環中創建線程,如果線程是保持的一般手機能堅持到1000個,等GC回收不能滿足分配需求會出現OOM異常;
4.我們項目最好使用線程池控制最大并發數,第三方較多時候,最好針對第三方hook thread,防止第三方頻繁創建線程,出現OOM。
情景在線
問:android中創建一個線程在內存中占用多大內存?
答:根據手機版本的不同,表現不同,一般是廠商定制,默認是1024kb大小
上面的回答,其實大部分人都覺得是正確的回答。其實這個是回答不正確!
先說答案
1.占用內存的大小是不確定的,跟當前線程執行的邏輯相關,后面我驗證,另外默認1M的大小是指棧幀的最大值,并不是創建線程,內存就增加了1M,我驗證了真機 模擬器創建一個啥都不做的線程大概內存占用增加30K左右,當然如果寫個死循環,在里面做別的,堆內存增加了就不好確認內存增加了多少
首先,創建一個線程在內存中占用大小是不確定的,先不說不是同一款手機,就算是同一款手機也區別很大。
然后,JVM虛擬機配置的參數 - Xss可以指定大小,這個配置是指什么
-Xss 256K
這個其實是設置棧幀的最大值,也可以說是遞歸調用方法,到遞歸到多少次,就會棧溢出。
JVM 棧規定了兩種溢出方式:1.遞歸調用,出現stackoverflowererror ;2.另外就是棧中也會跑出OOM
作為安卓開發其實很少遇到設置這個JVM參數
Xss 設置的大小決定了函數調用的深度,如果函數調用的深度大于設置的Xss大小,那么將會拋“java.lang.StackOverflowError“ 異常,
寫如下主要代碼驗證:
private String analyzeMemory() {
Runtime mRuntime = Runtime.getRuntime();
long usedMemory = mRuntime.totalMemory() - mRuntime.freeMemory();
Log.e("TAG", "analyzeMemory: "+ usedMemory );
return result;
}
private class MyThread extends Thread {
@Override
public void run() {
mHandler.sendMessage(mHandler.obtainMessage(1, analyzeMemory()));
while (true) {//讓線程保持一直運行
}
}
}
可以看到新建一個線程,內存增加大概30K,真機模擬器都差不多。
2021-09-09 19:10:35.543 31007-31665/com.android.projects.testthreadsize E/TAG: analyzeMemory: 2012584
2021-09-09 19:10:39.575 31007-31674/com.android.projects.testthreadsize E/TAG: analyzeMemory: 2029016
2021-09-09 19:10:42.506 31007-31682/com.android.projects.testthreadsize E/TAG: analyzeMemory: 2061832
2021-09-09 19:10:46.925 31007-31684/com.android.projects.testthreadsize E/TAG: analyzeMemory: 2094664
死循環創建線程
當代碼中一直創建線程,會怎樣?
一般能創建一千多個線程就會崩,會頻繁的GC,卡死,最后OOM。
for (int i = 0; i <1000000 ; i++) {
MyThread mMyThread = new MyThread();
threadList.add(mMyThread);
mMyThread.start();
}
線程里面一定要 讓線程一直運行,否則 一直創建,會一直回收
private class MyThread extends Thread {
@Override
public void run() {
mHandler.sendMessage(mHandler.obtainMessage(1, analyzeMemory()));
while (true) {//讓線程保持一直運行
}
}
}
可以看到下圖,棧內存不斷增加,因為我們在循環中MyThread mMyThread,這個mMyThread對象 會在棧內存上不斷增加,
當GC速度跟不上分配速度時候,日志如下:
可以看到創建到一千多個線程時候,仍然在頻繁的GC回收內存,
2021-09-09 20:34:40.599 14896-16968/com.android.projects.testthreadsize E/TAG: 1511analyzeMemory: 33359624
2021-09-09 20:34:40.599 14896-16968/com.android.projects.testthreadsize E/TAG: mRuntime.totalMemory(): 31 >>>>>0
2021-09-09 20:34:40.603 14896-16979/com.android.projects.testthreadsize I/.testthreadsiz: Waiting for a blocking GC Alloc
2021-09-09 20:34:40.603 14896-16978/com.android.projects.testthreadsize E/TAG: 1512analyzeMemory: 33458072
2021-09-09 20:34:40.603 14896-16978/com.android.projects.testthreadsize E/TAG: mRuntime.totalMemory(): 31 >>>>>0
2021-09-09 20:34:40.604 14896-16967/com.android.projects.testthreadsize E/TAG: 1513analyzeMemory: 33211880
2021-09-09 20:34:40.604 14896-16967/com.android.projects.testthreadsize E/TAG: mRuntime.totalMemory(): 31 >>>>>0
2021-09-09 20:34:40.604 14896-16734/com.android.projects.testthreadsize I/.testthreadsiz: Waiting for a blocking GC Alloc
2021-09-09 20:34:40.604 14896-16735/com.android.projects.testthreadsize I/.testthreadsiz: Waiting for a blocking GC Alloc
2021-09-09 20:34:40.605 14896-16736/com.android.projects.testthreadsize I/.testthreadsiz: Waiting for a blocking GC Alloc
2021-09-09 20:34:40.605 14896-16982/com.android.projects.testthreadsize I/.testthreadsiz: Waiting for a blocking GC Alloc
2021-09-09 20:34:40.620 14896-16981/com.android.projects.testthreadsize I/.testthreadsiz: Waiting for a blocking GC Alloc
2021-09-09 20:34:40.704 14896-16983/com.android.projects.testthreadsize I/.testthreadsiz: Waiting for a blocking GC Alloc
2021-09-09 20:34:40.709 14896-16976/com.android.projects.testthreadsize I/.testthreadsiz: Waiting for a blocking GC Alloc
2021-09-09 20:34:40.760 14896-14896/com.android.projects.testthreadsize I/.testthreadsiz: Waiting for a blocking GC Alloc
Background young concurrent copying GC freed 6206(6060KB) AllocSpace objects, 0(0B) LOS objects, 0% free, 378MB/378MB, paused 11.955ms total 85.664s
上面的最后一行代碼,378MB/378MB,內存使用已經到上線了。下一次申請超過剩余操作限制了就OOM了.我們就是用下面命令
zhouhao@zhouhaodeMacBook-Pro ~ % adb -s D5F7N18710001743 shell
HWCLT:/ $ getprop|grep heapgrowthlimit
[dalvik.vm.heapgrowthlimit]: [384m]
HWCLT:/ $
最終會報錯這個,出現pthread_create :
java.lang.OutOfMemoryError: pthread_create (1040KB stack) failed: Out of memory
at java.lang.Thread.nativeCreate(Native Method)
at java.lang.Thread.start(Thread.java:893)
at com.android.projects.testthreadsize.MainActivity$1.onClick(MainActivity.java:37)
at android.view.View.performClick(View.java:7192)
at android.view.View.performClickInternal(View.java:7166)
at android.view.View.access$3500(View.java:824)
at android.view.View$PerformClick.run(View.java:27592)
at android.os.Handler.handleCallback(Handler.java:888)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:213)
at android.app.ActivityThread.main(ActivityThread.java:8178)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:513)
上面的totalMemory() 其實指的是堆上的內存,當堆內存不足時候,還一直在創建線程,這邊一直GC,這個時候手機卡住了,然后一直還在創建,最后等到整個手機內存爆增后,虛擬內存不足,直接OOM。
有個疑問,為啥prefiler上的沒有增加!