3

Android性能:远程触发GC

 3 years ago
source link: http://yuanfentiank789.github.io/2016/09/09/remotegc/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

一、远程触发GC原理

我们都知道 GC 是java虚拟机释放内存的机制。

一般的在当前进程触发GC有两种方式:

  • 主动触发。调用System.gc()
  • 被动触发。预分配的内存不足 or OOM之前

有没有办法跨进程 做GC操作呢? 答案是肯定的。因为Android Moniter中就给我们提供了 Initiate GC 功能,可以对Debug版进程 远程触发GC动作:

这个功能是Android Studio中自带的, 到底是如何实现的呢? 通过阅读android源码, 发现了Android 框架中为每个进程都预留了 远程触发GC的接口:

// URL: http://androidxref.com/4.4.4_r1/xref/dalvik/vm/SignalCatcher.cpp#signalCatcherThreadStart
static void* signalCatcherThreadStart(void* arg)
{
// ... (略)
sigemptyset(&mask);
sigaddset(&mask, SIGUSR1);

// ... (略)

while (true) {
    // ...
    cc = sigwait(&mask, &rcvd);
    // ...
    if (gDvm.haltSignalCatcher)
        break;
    // ...
    switch (rcvd) {
    // ...
    case SIGUSR1:
        handleSigUsr1();
        break;
    // ...
    }
}

return NULL;
}

而 handleSigUsr1() 函数是干啥子的呢?

// http://androidxref.com/4.4.4_r1/xref/dalvik/vm/SignalCatcher.cpp#handleSigUsr1
static void handleSigUsr1()
{
  ALOGI("SIGUSR1 forcing GC (no HPROF)");
  dvmCollectGarbage(); /*GC函数*/
}

看到这里已经能确定, 可以通过发送信号 SIGUSR1 来触发GC了。 SIGUSR1对应的ID是多少呢?

//http://androidxref.com/4.4.4_r1/xref/bionic/libc/kernel/arch-arm/asm/signal.h#39
#define SIGUSR1 10

到此, 就确定了全程触发GC的方法: kill -10 PID

二、验证结论

俗话说,没有经过验证的结论都是耍流氓。 so, 本着严谨的态度, 写了一段代码来验证,如下:

public class MainActivity extends ActionBarActivity {

/*用来存放对象的List*/
static List<Object> list = new ArrayList<>();

/*用来创建软引用的类*/
static class SoftObj {
    public SoftObj() {
        System.out.println("SoftObj(" + this + ") inited");
    }

    @Override
    public void finalize() {
        System.out.println("SoftObj(" + this + ") finalize");
    }
}

 /*用来创建弱引用的类*/
static class WeakObj {
    public WeakObj() {
        System.out.println("WeakObj(" + this + ") inited");
    }

    @Override
    public void finalize() {
        System.out.println("WeakObj(" + this + ") finalize");
    }
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(com.ddlx.amaptesttools.R.layout.activity_main);

    /*创建 6 个软引用对象*/
    list.add(new SoftReference<Object>(new SoftObj()));
    list.add(new SoftReference<Object>(new SoftObj()));
    list.add(new SoftReference<Object>(new SoftObj()));
    list.add(new SoftReference<Object>(new SoftObj()));
    list.add(new SoftReference<Object>(new SoftObj()));
    list.add(new SoftReference<Object>(new SoftObj()));

    /*创建 6 个弱引用对象*/
    list.add(new WeakReference<Object>(new WeakObj()));
    list.add(new WeakReference<Object>(new WeakObj()));
    list.add(new WeakReference<Object>(new WeakObj()));
    list.add(new WeakReference<Object>(new WeakObj()));
    list.add(new WeakReference<Object>(new WeakObj()));
    list.add(new WeakReference<Object>(new WeakObj()));
}
}

编译成APK后,我在adbshell 中敲入了如下命令:

 C:\Users\sy101268>adb shell
shell@mx3:/ $ su
root@mx3:/ # ps | grep ddlx
u0_a316   188767 2136  923856 64372 ffffffff 4006f78c S com.ddlx
root@mx3:/ # kill -10 18767

通过Logcat 看到了如下日志:

// APK启动时输出的日志
07-19 16:03:22.290 18767-18767/com.ddlx  I/System.out:     SoftObj(com.ddlx.MainActivity$SoftObj@427d92c0) inited
07-19 16:03:22.290 18767-18767/com.ddlx  I/System.out: SoftObj(com.ddlx.MainActivity$SoftObj@427d9920) inited
07-19 16:03:22.295 18767-18767/com.ddlx  I/System.out: SoftObj(com.ddlx.MainActivity$SoftObj@427d9d08) inited
07-19 16:03:22.295 18767-18767/com.ddlx  I/System.out: SoftObj(com.ddlx.MainActivity$SoftObj@427da0f0) inited
07-19 16:03:22.295 18767-18767/com.ddlx  I/System.out: SoftObj(com.ddlx.MainActivity$SoftObj@427da4d8) inited
07-19 16:03:22.295 18767-18767/com.ddlx  I/System.out: SoftObj(com.ddlx.MainActivity$SoftObj@427da8c0) inited

07-19 16:03:22.295 18767-18767/com.ddlx  I/System.out: WeakObj(com.ddlx.MainActivity$WeakObj@427daf68) inited
07-19 16:03:22.295 18767-18767/com.ddlx  I/System.out: WeakObj(com.ddlx.MainActivity$WeakObj@427db428) inited
07-19 16:03:22.295 18767-18767/com.ddlx  I/System.out: WeakObj(com.ddlx.MainActivity$WeakObj@427db810) inited
07-19 16:03:22.295 18767-18767/com.ddlx  I/System.out: WeakObj(com.ddlx.MainActivity$WeakObj@427dbbf8) inited
07-19 16:03:22.295 18767-18767/com.ddlx  I/System.out: WeakObj(com.ddlx.MainActivity$WeakObj@427dbfe0) inited
07-19 16:03:22.295 18767-18767/com.ddlx  I/System.out: WeakObj(com.ddlx.MainActivity$WeakObj@427dc3c8) inited

/*********************************************************/

// 调用 kill -10 命令后,输出的日志
07-19 16:04:25.795 18767-18772/com.ddlx  I/dalvikvm: SIGUSR1 forcing GC(no HPROF)
07-19 16:04:25.835 18767-18772/com.ddlx  D/dalvikvm: GC_EXPLICIT freed 94K, 5% free 17788K/18624K, paused 3ms+4ms, total 39ms

07-19 16:04:25.840 18767-18776/com.ddlx  I/System.out: SoftObj(com.ddlx.MainActivity$SoftObj@427d9920) finalize
07-19 16:04:25.840 18767-18776/com.ddlx  I/System.out: SoftObj(com.ddlx.MainActivity$SoftObj@427da0f0) finalize
07-19 16:04:25.840 18767-18776/com.ddlx  I/System.out: SoftObj(com.ddlx.MainActivity$SoftObj@427da8c0) finalize

07-19 16:04:25.840 18767-18776/com.ddlx  I/System.out: WeakObj(com.ddlx.MainActivity$WeakObj@427daf68) finalize
07-19 16:04:25.840 18767-18776/com.ddlx  I/System.out: WeakObj(com.ddlx.MainActivity$WeakObj@427db428) finalize
07-19 16:04:25.840 18767-18776/com.ddlx  I/System.out: WeakObj(com.ddlx.MainActivity$WeakObj@427db810) finalize
07-19 16:04:25.840 18767-18776/com.ddlx  I/System.out: WeakObj(com.ddlx.MainActivity$WeakObj@427dbbf8) finalize
07-19 16:04:25.840 18767-18776/com.ddlx  I/System.out: WeakObj(com.ddlx.MainActivity$WeakObj@427dbfe0) finalize
07-19 16:04:25.840 18767-18776/com.ddlx  I/System.out: WeakObj(com.ddlx.MainActivity$WeakObj@427dc3c8) finalize

至此, 验证通过了~,一切看上去很完美~。。。 等等, 为啥 SoftObj会被回收? 并且只回收了一半?Java书上不是这么说的哇

揭开这个谜底就需要对Android虚拟机有一定的了解了。 我们知道Android的虚拟机是定制的,基于移动平台的解决方案。移动平台上,内存往往并不那么充足,所以google改变了内存回收策略。

Android上对于软引用, 改成了每次GC都回收一半的策略。(在Java HotSpot虚拟机中, 软引用只有在OOM触发前时候才会被回收)



About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK