android中调用native的fork函数后的现象观察

疑问

android的app运行过程中,如果通过jni调用到了native层的fork()函数,那么java层的各对象都会复制一份吗?

建工程做实验

建立一个包含C++的android工程,在工程中的native-lib.cpp中改成这个

1
2
3
4
5
6
7
8
9
10
#include <jni.h>
#include <string>
#include <unistd.h>

extern "C" JNIEXPORT jint JNICALL
Java_mainpackage_MainActivity_invokeFork(
JNIEnv *env,
jobject /* this */) {
return fork();
}

然后在kotlin中建立一个class

1
2
3
4
5
6
7
8
9
package mainpackage

class SomeBody(var name: String) {
override fun toString(): String {
return "($name ${System.identityHashCode(this).toHex()})"
}

private fun Int.toHex() = Integer.toHexString(this)
}

然后在MainActivity中写一个函数

1
2
3
4
5
6
fun studyFork() {
val sb = SomeBody("张三")
Log.i("fork", "before fork >> sb: $sb")
val forkRet = invokeFork()
Log.i("fork", "after fork >> forkRet: $forkRet pid: ${Process.myPid()} sb: $sb")
}

并且在MainActivity被create之后,调用studyFork()
发现输出是:

1
2
3
before fork >> sb: (张三 3e8825b)
after fork >> forkRet: 21281 pid: 21262 sb: (张三 3e8825b)
after fork >> forkRet: 0 pid: 21281 sb: (张三 3e8825b)

观察结果,得出自己的判断,对象sb在主进程和子进程中是同一个,即主进程和子进程共享同一个对象(后面的实验推翻了这个结论)。

新的困惑

主进程和子进程共享虚拟机中同一个堆空间吗?两个进程操作这个对象,观察到的结果是什么呢?
继续做实验,修改了studyFork()函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fun studyFork() {
val sb = SomeBody("张三")

Log.i("fork", "before fork >> sb: $sb")
val forkRet = invokeFork()
Log.i("fork", "after fork >> forkRet: $forkRet pid: ${Process.myPid()} sb: $sb")

if (forkRet == 0) { // 子进程
sb.name = "李四"
Log.i("fork", "sub process >> forRet: $forkRet modified-sb: $sb")
} else { // 父进程
Thread.sleep(1000)
Log.i("fork", "parent process >> forRet: $forkRet sb: $sb")
}
}

让子进程创建后立刻修改sb的name为“李四”,然后父进程1s之后观察这个sb并打印日志。日志如下:

1
2
3
4
5
2019-04-03 18:33:24.208  I/fork: before fork >> sb: (张三 3e8825b)
2019-04-03 18:33:24.214 I/fork: after fork >> forkRet: 0 pid: 22143 sb: (张三 3e8825b)
2019-04-03 18:33:24.214 I/fork: after fork >> forkRet: 22143 pid: 22123 sb: (张三 3e8825b)
2019-04-03 18:33:24.215 I/fork: sub process >> forRet: 0 modified-sb: (李四 3e8825b)
2019-04-03 18:33:25.219 I/fork: parent process >> forRet: 22143 sb: (张三 3e8825b)

于是得出自己的判断:子进程修改了sb对象的name,主进程感知不到。进而得出这样的结论,主进程和子进程不共享同一个对象,只是各自持有一份sb拷贝。进而,两个进程不共享虚拟机的同一个堆空间。