swig director内存管理

swig中的director是一个由c/c++往上层语言调用的封装。以下笔者将以java为上层语言讲解director的内存管理。

Demo源码

先列举代码:

my.h

#ifndef MY_H
#define MY_H

#include <string>

class My
{
public:
    virtual ~My(){}
    virtual std::string text(const std::string& str) = 0;
};

void doIt(My* my);

#endif

my.i

%module(directors="1") "my.i"
%{
#include "my.h"
%}

%include "std_string.i"

%feature("director", assumeoverride=1) My;

%include "my.h"

这是一个比较简单的代码:通过swig导给java一个doIt函数,并要求传入一个回调类MyMy要做的事情就是当text被调用时,返回一个string。

ownership

swig中针对director设计了一个ownership的概念。在生成的java代码中有swigTakeOwnership/swigReleaseOwnership管理其ownership。

需要明确的是ownership是针对java而言的,即jvm是否拥有其内存。

构造后的ownership

那么,当java代码new一个director的时候,默认是否ownership为true呢?是的。看以下代码:

public class My {
  private transient long swigCPtr;
  protected transient boolean swigCMemOwn;

  protected My(long cPtr, boolean cMemoryOwn) {
    swigCMemOwn = cMemoryOwn;
    swigCPtr = cPtr;
  }
  ...
  public My() {
    this(myJNI.new_My(), true);
    myJNI.My_director_connect(this, swigCPtr, swigCMemOwn, true);
  }
}

public构造函数指定其swigCMemOwn为true, 并调用My_director_connect指定weak_global为true。接着My_director_connect -> Java_com_cy_swig_myJNI_My_1director_1connect -> director::swig_connect_director -> swig_set_self -> swig_self_.set

看下swig_self_.set:

bool set(JNIEnv *jenv, jobject jobj, bool mem_own, bool weak_global) {
      if (!jthis_) {
        weak_global_ = weak_global || !mem_own; // hold as weak global if explicitly requested or not owned
        if (jobj)
          jthis_ = weak_global_ ? jenv->NewWeakGlobalRef(jobj) : jenv->NewGlobalRef(jobj);
        return true;
      } else {
        return false;
      }
    }

jthis_一开始为空,因为mem_own=true, weak_global=true, 所以weak_global_=true,所以创建了一个weak global reference。由于是弱引用,所以内存管理是java来管的。

swigReleaseOwnership

刚才提到构造后java默认拥有内存,那么先看看swigReleaseOwnership后,内存是如何移交给c++的。

调用链:swigReleaseOwnership -> myJNI.My_change_ownership(this, swigCPtr, false); -> Java_com_cy_swig_myJNI_My_1change_1ownership -> director::swig_java_change_ownership -> swig_self_.java_change_ownership

那么来看下java_change_ownership:

/* Java proxy releases ownership of C++ object, C++ object is now
       responsible for destruction (creates NewGlobalRef to pin Java proxy) */
    void java_change_ownership(JNIEnv *jenv, jobject jself, bool take_or_release) {
      if (take_or_release) {  /* Java takes ownership of C++ object's lifetime. */
        ......
      } else {
    /* Java releases ownership of C++ object's lifetime */
        if (weak_global_) {
          jenv->DeleteWeakGlobalRef((jweak)jthis_);
          jthis_ = jenv->NewGlobalRef(jself);
          weak_global_ = false;
        }
      }
    }

首先看注释,C++ object is now responsible for destruction,意思是内存的释放要c++来做。再看如何实现的,由于take_or_release=false,进入else分支,构造时weak_global_=true,所以将弱引用删除,构造强引用,weak_global_=false。这样就完成了内存管理的移交。

但是,还有一个问题,c++ 是如何responsible for destruction呢?

实际上这个责任是doIt要做的,也就是回调My的“库”要在不再需要回调它的时候delete。比如这个doIt实现:

void doIt(My* my)
{
    my->text("hello");
    delete my;
}

但是,还是有一个问题,之前创建的强引用,何时释放呢?

实际上,swig很聪明地利用多继承,自动了生成了一个SwigDirector_My:class SwigDirector_My : public My, public Swig::Director,并将这个类的指针保存在java中的My.swigCPtr。当doIt拿到My指针时,实际上是SwigDirector_My,所以delete my同时调用了Director的析构。

看看Director析构是怎么做的:

virtual ~Director() {
      JNIEnvWrapper jnienv(this) ;
      JNIEnv *jenv = jnienv.getJNIEnv() ;
      swig_self_.release(jenv);
    }

//swig_self_
void release(JNIEnv *jenv) {
      if (jthis_) {
        if (weak_global_) {
          if (jenv->IsSameObject(jthis_, NULL) == JNI_FALSE)
            jenv->DeleteWeakGlobalRef((jweak)jthis_);
        } else
          jenv->DeleteGlobalRef(jthis_);
      }

      jthis_ = NULL;
      weak_global_ = true;
    }

很明显,release中不论强弱引用,都正确释放了。

swigTakeOwnership

调用链类似,直接看java_change_ownership

    void java_change_ownership(JNIEnv *jenv, jobject jself, bool take_or_release) {
      if (take_or_release) {  /* Java takes ownership of C++ object's lifetime. */
        if (!weak_global_) {
          jenv->DeleteGlobalRef(jthis_);
          jthis_ = jenv->NewWeakGlobalRef(jself);
          weak_global_ = true;
        }
      } else {
          ......
      }
    }

take_or_release=true,weak_global_=false(假设先release了ownership,再take,如果构造完马上调这个函数就等于什么也没做),所以删除强引用,创建弱引用。

弱引用的释放呢?

java“析构函数”finalize调用了delete,接着delete_My -> Java_com_cy_swig_myJNI_delete_1My

SWIGEXPORT void JNICALL Java_com_cy_swig_myJNI_delete_1My(JNIEnv *jenv, jclass jcls, jlong jarg1) {
  My *arg1 = (My *) 0 ;

  (void)jenv;
  (void)jcls;
  arg1 = *(My **)&jarg1; 
  delete arg1;
}

没错,聪明的读者已经想到了,这里的My实际上是SwigDirector_My,delete arg1会触发Director的析构。

至此,director的内存管理核心部分就分析完了。

要不要ownership

根据上面的分析,很容易得出一条原则:要不要ownership,取决于谁负责销毁内存。如果c++在使用完回调后会自己销毁内存,那java创建好director就releaseOwnership,否则,持有ownership。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值