android 内存回收

  昨天朋友问我,如果一个java局部对象在调用jni的时候,如果java层没有引用它,这个对象会不会因为被jni层引用不被GC,导致内存泄漏。我大概想了一下,说不会。当时想的很简单,c里面没有像java一样的类似的内存回收机制,java层进入jni时值传递,不会导致引用产生。实事上比想象的复杂的多,而且并不是这样的。  

  按照这种想法,如果java层的对象被jni引用时不会计数,对象被GC时,jni层会产生野指针。事实并不是这样。

  首先GC是发生在进程范围内的堆空间,如果堆里的对象没有被引用,是要被GC回收的。jni层也会在堆上创建对象,照样能被GC。

  java的内存回收可以用下图表示。

  

如果heap里面的对象没有被Stack引用到,即红色的区域,是要被回收的。总的来说,只要对象没有被GC root直接或间接引用到就会被GC。

jni调用产生时,jvm的内存分布是这个样子的。

它有两个栈,一个是java栈,另外一个是native 栈。当一个java调用jni时,栈帧布局其实是下图这个样子。

这个是栈帧示意图,上图所示,该线程首先调用了两个Java方法,而第二个Java方法又调用了一个本地方法,这样导致虚拟机使用了一个本地方法栈。图中的本地方法栈显示为 一个连续的内存空间。假设这是一个C语言栈,期间有两个C函数,他们都以包围在虚线中的灰色块表示。第一个C函数被第二个Java方法当做本地方法调用, 而这个C函数又调用了第二个C函数。之后第二个C函数被第二个Java方法当做本地方法调用,而这个C函数又调用了第二个C函数。之后第二个C函数又通过 本地方法接口回调了一个Java方法(第三个Java方法)。最终这个Java方法又调用了一个Java方法(他成为图中的当前方法) 。

 

刚才说到只要对象没有被GC root直接或间接引用到就会被GC。GC root到底是什么呢?

JVM对那些没有根引用的对象进行来及回收,也就是无法从根对象中追述的对象。
JVM垃圾回收的根对象的范围有以下几种:
1、栈中引用的对象,引用是在栈帧中的本地变量表中的,真正的对象在堆中
2、方法区perm中的类静态属性引用的对象,以及常量引用的对象
3、本地方法栈中JNI(Native方法)的引用的对象

所以jni层,C函数调用C函数传递jobject对象时,对象的引用会被作为参数压入到本地方法栈中,会从本地方法栈(GC root的一种)产生引用,这么就不会被gc了。同样java层和jni层互相调用时,总会被GC root引用到,也不会被GC 回收了。

posted on 2016-04-14 13:05  各各他  阅读(2446)  评论(0编辑  收藏  举报

导航