疑问
android的app运行过程中,如果通过jni调用到了native层的fork()函数,那么java层的各对象都会复制一份吗?
建工程做实验
建立一个包含C++的android工程,在工程中的native-lib.cpp中改成这个1
2
3
4
5
6
7
8
9
10
extern "C" JNIEXPORT jint JNICALL
Java_mainpackage_MainActivity_invokeFork(
JNIEnv *env,
jobject /* this */) {
return fork();
}
然后在kotlin中建立一个class1
2
3
4
5
6
7
8
9package 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
6fun 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
3before 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
15fun 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
52019-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拷贝。进而,两个进程不共享虚拟机的同一个堆空间。