误区:Android一个线程占用多大内存

结论

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参数

image.png

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对象 会在栈内存上不断增加,


image.png

当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上的没有增加!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,012评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,589评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,819评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,652评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,954评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,381评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,687评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,404评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,082评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,355评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,880评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,249评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,864评论 3 232
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,007评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,760评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,394评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,281评论 2 259

推荐阅读更多精彩内容