Android的进程、线程与优先级

来源:互联网 发布:js代码获取ip地址 编辑:程序博客网 时间:2024/04/28 06:41

一、结论

1、Android中进程的优先级与垃圾回收机制相关,优先级越低被垃圾回收的机会越大。当内存不足的时候,总是低优先级的进程被最先回收;

2、Android中线程的优先级与调用顺序有关,优先级越高被调用的可能性越高(注意,是可能性更高),也就是说即使线程A的优先级大于线程B,同等情况下线程A不一定先于线程B被调用。


二、进程与线程

1、什么是进程、线程

如果你想要一个程序运行得快,那么可以将其断开为多个片段,在单独的处理器上运行每个片段,这是并发编程最主要的考虑,简单理解:

  • 进程(process):是一块包含了某些资源的内存区域,是操作系统的最小可执行单元。操作系统利用进程把它的工作划分为一些功能单元。一个程序至少对应一个进程。
  • 线程(thread):是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。一个进程至少对应一个线程。
    这里写图片描述

在Android中,每个app运行时前首先创建一个进程,该进程是由Zygote fork出来的,用于承载App上运行的各种Activity/Service等组件。大多数情况一个App就运行在一个进程中,除非:

(1)配置Android:process属性:

a.进程名为com.goodong.com.process1:

    <activity        android:name=".MyFirstActivity"        android:process="com.goodong.com.process1"        >        <intent-filter>            ……        </intent-filter>    </activity>

b.配置进程名为”:process2”(包名+process2):

    <activity        android:name=".MyFirstActivity"        android:process=":process2"        >        <intent-filter>……            </intent-filter>   </activity>

注意:第2种命名进程的方式与第1中命名方式的区别在于,后者不能做到:让不同应用程序的组件运行在同一个进程中。

(2)通过native代码fork进程:

应用程序的进程是由Zygote创建的,在ActivityManagerService中的startProcessLocked中调用了Process.start()方法。并通过连接调用Zygote的native方法forkAndSpecialize,执行fork任务。

2、线程的调度

线程对应用来说非常常见,比如每次new Thread().start都会创建一个新的线程。该线程与App所在进程之间资源共享,从Linux角度来说进程与线程除了是否共享资源外,并没有本质的区别。

Java的线程机制是抢占式的,这表示调度机制会周期性地中断线程,将上下文切换到另一个线程,从而为每个线程都提供时间片,使得每个线程都会分配到数量合理的时间去执行它的任务

CPU轮流为每个任务分配其占用时间。每个任务都觉得自己在一直占用CPU,但事实上CPU时间是划分成片段分配给了所有任务。使用线程机制是一种建立透明的、可扩展的程序的方法,如果程序运行太慢,为机器添加一个CPU就能很容易地加快程序的运行速度。

请注意,尽管多任务和多线程往往是使用多处理器系统的最合理方式,但多线程编程并不是仅仅针对多处理器,即使在单处理上,并发编程也是有用武之地的。这是因为,如果一个程序包含了多个顺序执行的任务(不是并发执行),因为每个任务都可能被阻塞,一旦任务被阻塞,程序就停止执行。但在并发编程下,多个任务并发执行,单任务阻塞并不会直接导致程序停止执行


三、Android进程的优先级

1、优先级意味着什么

优先级的意思是:针对多个对象的某种操作的执行顺序。上面我们说过,进程是一块内存区域,因为对进程而言,优先级意味着何时释放资源:

  • 在释放进程资源的时候,让优先级低的进程先释放释放资源;
  • 如果即将被运行的进程的优先级比正在运行的进程的优先级高,则系统可以强行剥夺正在运行的进程的资源,让优先级高的进程先运行。

2、有多少个优先级

在Android 中,进程的优先级就是oom_adj的值,而oom_adj被定义在init.rc中:

· Define the memory thresholds at which the above process classes will· be killed.  These numbers are in pages (4k).    setprop ro.FOREGROUND_APP_ADJ         0    setprop ro.VISIBLE_APP_ADJ            1    setprop ro.SECONDARY_SERVER_ADJ       2    setprop ro.HIDDEN_APP_MIN_ADJ         7    setprop ro.CONTENT_PROVIDER_ADJ       14    setprop ro.EMPTY_APP_ADJ              15

名称 oom_adj 解释:
这里写图片描述

3、oom_adj值会随着进程的状态变化而变化

adb连接手机后,笔者从桌面上启动了知乎,用adb shell dumpsys activity查看activitys的分布,可以看到activity的次序如下:
这里写图片描述
这时候另开一个命令行窗口查看进程的进程号:
这里写图片描述
这个时候知乎有两个进程,分别是15345和15725,使用cat /proc//oom_adj 命令查看它们的oom_adj值:
它们的值分别是0和11(oom_adj值是可以修改);
这里写图片描述
将知乎退到后台再查询它们的oom_adj值:
两个进程的值分别是9和13(退后台前是0和11)。
这里写图片描述

4、如何根据oom_adj的值判断回收时机

init.rc中定义垃圾回收的阈值:

· Write value must be consistent with the above properties.· Note that the driver only supports 6 slots, so we have combined some of· the classes into the same memory level; the associated processes of higher· classes will still be killed first.·写入的值必须符合上面的属性。注意设备只支持6个等级,所以某些·类会被合并到同一个等级中。拥有更高等级的进程将被优先杀死。&nbsp; &nbsp; setprop ro.FOREGROUND_APP_MEM     1536(6M)&nbsp; &nbsp; setprop ro.VISIBLE_APP_MEM           2048(8M)&nbsp; &nbsp; setprop ro.SECONDARY_SERVER_MEM    4096(16M)&nbsp; &nbsp; setprop ro.HIDDEN_APP_MEM           5120(20M)&nbsp; &nbsp; setprop ro.CONTENT_PROVIDER_MEM    5632(22M)&nbsp; &nbsp; setprop ro.EMPTY_APP_MEM            6144(24M)

这些数字也就是对应的内存阈值,一旦低于该值,Android便开始按顺序关闭相应的进程 。具体的回收实现在ActivityManagerService.java中的函数trimApplications():

  • 首先移除package被移走的无用进程;

基于进程当前状态,更新oom_adj值,然后进行以下操作:

  • 移除没有activity在运行的进程。如果APP已经保存了所有的activity状态,结束这个APP;
  • 最后,如果目前还是有很多activities 在运行,那么移除那些activity状态已经保存好的activity。

当系统内存短缺时Android的Low Memory Killer根据需要杀死进程释放其内存,简单说就是寻找一个最合适的进程杀死,从而释放它占用的内存,最合适指的是:

  • oom_adj越大
  • 占用物理内存越多

四、Android线程的优先级

1、线程的简单示范

从CPU的角度看,Android线程就是Java线程。因而Android线程的优先级就是Java线程的优先级(但在Android开发中,笔者似乎从未操心过线程的优先级)。
Java中使用线程最常用的方法是:

  • 实现Runnable接口,覆写run()方法;
  • 继承Thread类,覆写run()方法(实际也是实现Runnable接口)。
    先实现实现Runnable接口:

    public class RunabbleImp implements Runnable{    @Overridepublic void run() {doSomeThing();}private void doSomeThing() {System.out.println(Thread.currentThread().getName()+" is running!");    }}

测试一下:

    public class ThreadTest {        public static void main(String[] args) {            RunabbleImp runabbleImp = new RunabbleImp();            runabbleImp.run();            Thread thread = new Thread(new RunabbleImp());            thread.start();        }    }

打印结果:

    main is running!    Thread-0 is running!

可以看出runabbleImp.run();实际上是在主线程中运行,而thread.start();已经开启了一个新线程。

2、线程的优先级

线程的优先级将线程的重要性传递给调度器。尽管CPU处理现有线程集的顺序是不确定的,但是调度器将倾向于让优先级高的线程先执行。然而,这并不意味着优先权低的线程将得不到执行(也就是说,优先级不会导致死锁)。优先级较低的线程仅仅是执行的频率较低。
可以用getPriority()获取当前线程的优先级,并且在任何时刻都可以通过setPriority()来修改它:

public class PriorityThread implements Runnable {        private int timeCount = 5;        private int priority;    public PriorityThread(int priorityIn) {    priority = priorityIn;    }@Overridepublic String toString() {    return Thread.currentThread() +":"+timeCount;}@Overridepublic void run() {    Thread.currentThread().setPriority(priority);    while(true){        for (int i = 0; i < 100000; i++) {            double d = (Math.PI + Math.E)/(double)i;            if(i % 1000 == 0){                Thread.yield();            }        }        System.out.println(this);        if(--timeCount ==0){            break;        }    }}public class ThreadTest {    public static void main(String[] args) {        ExecutorService exec = Executors.newCachedThreadPool();        for (int i = 0; i < 5; i++) {            exec.execute(new PriorityThread(Thread.MIN_PRIORITY + i));        }    }    exec.execute(new PriorityThread(Thread.MAX_PRIORITY));    exec.shutdown();    }

部分打印结果如下:

        ……        Thread[pool-1-thread-6,10,main]:1        ……        Thread[pool-1-thread-5,5,main]:1        ……        Thread[pool-1-thread-3,3,main]:1        Thread[pool-1-thread-1,1,main]:1        ……        Thread[pool-1-thread-2,2,main]:1        Thread[pool-1-thread-4,4,main]:1

笔者执行多次后结果显示Thread[pool-1-thread-6,10,main]总是最先执行完毕,其他线程执行完毕顺序大致上是优先级高的先执行完毕,但是无法保证。

0 0
原创粉丝点击