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