誤區(qū):Android一個(gè)線程占用多大內(nèi)存

結(jié)論

1.創(chuàng)建一個(gè)線程,并不是會(huì)直接增加1M內(nèi)存,如果創(chuàng)建的是一個(gè)不退出的空線程,在華為P20pro、p40pro手機(jī)上,大致32Kb左右;
2.網(wǎng)上說(shuō)1M的文章,大部分是使用Runtime API獲取內(nèi)存大小,一般是因?yàn)閯?chuàng)建了對(duì)象 或者做了其他才會(huì)有這么大;
3.死循環(huán)中創(chuàng)建線程,如果線程是保持的一般手機(jī)能堅(jiān)持到1000個(gè),等GC回收不能滿足分配需求會(huì)出現(xiàn)OOM異常;
4.我們項(xiàng)目最好使用線程池控制最大并發(fā)數(shù),第三方較多時(shí)候,最好針對(duì)第三方hook thread,防止第三方頻繁創(chuàng)建線程,出現(xiàn)OOM。

情景在線

問(wèn):android中創(chuàng)建一個(gè)線程在內(nèi)存中占用多大內(nèi)存?
答:根據(jù)手機(jī)版本的不同,表現(xiàn)不同,一般是廠商定制,默認(rèn)是1024kb大小
上面的回答,其實(shí)大部分人都覺(jué)得是正確的回答。其實(shí)這個(gè)是回答不正確!

先說(shuō)答案

1.占用內(nèi)存的大小是不確定的,跟當(dāng)前線程執(zhí)行的邏輯相關(guān),后面我驗(yàn)證,另外默認(rèn)1M的大小是指棧幀的最大值,并不是創(chuàng)建線程,內(nèi)存就增加了1M,我驗(yàn)證了真機(jī) 模擬器創(chuàng)建一個(gè)啥都不做的線程大概內(nèi)存占用增加30K左右,當(dāng)然如果寫(xiě)個(gè)死循環(huán),在里面做別的,堆內(nèi)存增加了就不好確認(rèn)內(nèi)存增加了多少
首先,創(chuàng)建一個(gè)線程在內(nèi)存中占用大小是不確定的,先不說(shuō)不是同一款手機(jī),就算是同一款手機(jī)也區(qū)別很大。
然后,JVM虛擬機(jī)配置的參數(shù) - Xss可以指定大小,這個(gè)配置是指什么
-Xss 256K 這個(gè)其實(shí)是設(shè)置棧幀的最大值,也可以說(shuō)是遞歸調(diào)用方法,到遞歸到多少次,就會(huì)棧溢出。
JVM 棧規(guī)定了兩種溢出方式:1.遞歸調(diào)用,出現(xiàn)stackoverflowererror ;2.另外就是棧中也會(huì)跑出OOM
作為安卓開(kāi)發(fā)其實(shí)很少遇到設(shè)置這個(gè)JVM參數(shù)

image.png

Xss 設(shè)置的大小決定了函數(shù)調(diào)用的深度,如果函數(shù)調(diào)用的深度大于設(shè)置的Xss大小,那么將會(huì)拋“java.lang.StackOverflowError“ 異常,

寫(xiě)如下主要代碼驗(yàn)證:


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) {//讓線程保持一直運(yùn)行

            }
        }
    }
    

可以看到新建一個(gè)線程,內(nèi)存增加大概30K,真機(jī)模擬器都差不多。

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

死循環(huán)創(chuàng)建線程

當(dāng)代碼中一直創(chuàng)建線程,會(huì)怎樣?
一般能創(chuàng)建一千多個(gè)線程就會(huì)崩,會(huì)頻繁的GC,卡死,最后OOM。

 for (int i = 0; i <1000000 ; i++) {
                    MyThread mMyThread = new MyThread();
                    threadList.add(mMyThread);
                    mMyThread.start();
                }

線程里面一定要 讓線程一直運(yùn)行,否則 一直創(chuàng)建,會(huì)一直回收

 private class MyThread extends Thread {
        @Override
        public void run() {
            mHandler.sendMessage(mHandler.obtainMessage(1, analyzeMemory()));

            while (true) {//讓線程保持一直運(yùn)行

            }
        }
    }

可以看到下圖,棧內(nèi)存不斷增加,因?yàn)槲覀冊(cè)谘h(huán)中MyThread mMyThread,這個(gè)mMyThread對(duì)象 會(huì)在棧內(nèi)存上不斷增加,


image.png

當(dāng)GC速度跟不上分配速度時(shí)候,日志如下:
可以看到創(chuàng)建到一千多個(gè)線程時(shí)候,仍然在頻繁的GC回收內(nèi)存,

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,內(nèi)存使用已經(jīng)到上線了。下一次申請(qǐng)超過(guò)剩余操作限制了就OOM了.我們就是用下面命令

zhouhao@zhouhaodeMacBook-Pro ~ % adb  -s D5F7N18710001743 shell
HWCLT:/ $ getprop|grep heapgrowthlimit
[dalvik.vm.heapgrowthlimit]: [384m]
HWCLT:/ $ 

最終會(huì)報(bào)錯(cuò)這個(gè),出現(xiàn)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() 其實(shí)指的是堆上的內(nèi)存,當(dāng)堆內(nèi)存不足時(shí)候,還一直在創(chuàng)建線程,這邊一直GC,這個(gè)時(shí)候手機(jī)卡住了,然后一直還在創(chuàng)建,最后等到整個(gè)手機(jī)內(nèi)存爆增后,虛擬內(nèi)存不足,直接OOM。
有個(gè)疑問(wèn),為啥prefiler上的沒(méi)有增加!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容