C/CPP 调用 JNI 的区别

C++支持真正的对象而C不支持对象,所有C和C++调用JNI函数的时候会有一些细微的区别。

C++:

JavaVM* vm
JNIEnv* env = NULL;

vm->GetEnv((void**)&env, JNI_VERSION_1_6);
env->FindClass("main");

C:

JavaVM* vm
JNIEnv* env = NULL;

(*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6);
(*env)->FindClass(env, "main");

使用 Invocation API 调用 Java 类

InvocationApiTest.java

import java.io.*;

public class InvocationApiTest {
    public static void main(String[] args) {
        System.out.println(args[0]);
    }
}

invocationApi.c

#include <jni.h>

int main() {
    JNIEnv* env;
    JavaVM* vm;
    JavaVMInitArgs vm_args;
    JavaVMOption options[1];
    jint res;
    jclass cls;
    jmethodID mid;
    jstring jstr;
    jclass stringClass;
    jobjectArray args;

    //1.生成Java虚拟机选项
    options[0].optionString = "-Djava.class.path=.";
    vm_args.version = JNI_VERSION_1_6;
    vm_args.options = options;
    vm_args.nOptions = 1;
    vm_args.ignoreUnrecognized = JNI_TRUE;

    //2.生成Java虚拟机
    res = JNI_CreateJavaVM(&vm, (void**)&env, &vm_args);

    //3.查找并加载类
    cls = (*env)->FindClass(env, "invocationApiTest");

    //4.获取mian()方法的ID
    mid = (*env)->GetStaticMethodID(env, cls, "main", "([Ljava/lang/String;)V");

    //5.生成字符串对象,用作main()方法的参数
    jstr = (*env)->NewStringUTF(env, "Hello Invocation API");
    stringClass = (*env)->FindClass(env, "java/lang/String");
    args = (*env)->NewObjectArray(env, 1, stringClass, jstr);

    //6.调用main()方法的
    (*env)->CallStaticVoidMethod(env, cls, mid, args);

    //7.销毁Java虚拟机
    (*vm)->DestroyJavaVM(vm);

    return 0;
}

MAC上的编译参数:

gcc -Wall -g -o invocationApi invocationApi.c -I$JAVA_HOME/include  -I$JAVA_HOME/include/darwin  -L$JAVA_HOME/jre/lib/server -ljvm -rpath $JAVA_HOME/jre/lib/server

ref: jdk-7u45-apidocs/technotes/guides/jni/spec/invocation.html#wp9502 ref: 《Android框架揭秘》

获取 delete[]中的数组大小

看一个小例子:

#include <iostream>

using namespace std;

class A {
public:
    A() { cout << "A::A()" << endl; }
    ~A() { cout << "A::~A()" << endl; }
};

int main() {
    A* a = new A[5];
    delete[] a;

    return 0;
}

编译执行输出:

A::A()
A::A()
A::A()
A::A()
A::A()
A::~A()
A::~A()
A::~A()
A::~A()
A::~A()

上面的输出显示程序正确释放了5个A对象。 现在的问题是:我们在delete[] a语句中并没有给出对象数组的大小,那么系统是如何做到正确释放对象个数的呢?

接着看下面的程序段:

#include <iostream>

using namespace std;

class A {
public:
    A() { cout << "A::A()" << endl; }
    ~A() { cout << "A::~A()" << endl; }
};

int main() {
    A* a = new A[5];

    int addr = (int)a - 4;
    cout << "delete[] size = " << *(int*)addr << endl;

    delete[] a;

    return 0;
}

编译执行:

A::A()
A::A()
A::A()
A::A()
A::A()
delete[] size = 5
A::~A()
A::~A()
A::~A()
A::~A()
A::~A()

结论: 系统在new一个数组对象的时候会把数组大小存放在返回的内存地址的前4个字节中

linux C 获取线程号

#include <iostream>
#include <sys/types.h>
#include <pthread.h>
#include <sys/syscall.h>

using namespace std;

pid_t gettid() {

    return syscall(SYS_gettid);
}

int main() {

    pid_t t;
    t = gettid();
    cout << "pid_t = " << t << endl;
    return 0;
}

/proc/pid/status各项定义

Name: gedit /*进程的程序名*/
State: S (sleeping) /*进程的状态信息*/
Tgid: 9744 /*线程组号*/
Pid: 9744 /*进程pid*/
PPid: 7672 /*父进程的pid*/
TracerPid: 0 /*跟踪进程的pid*/
Uid: 1000    1000    1000    1000 /*uid euid suid fsuid*/
Gid: 1000    1000    1000    1000 /*gid egid sgid fsgid*/
FDSize: 256 /*文件描述符的最大个数,file->fds*/
Groups: 0 4 20 24 25 29 30 44 46 107 109 115 124 1000 /*启动该进程的用户所属的组的id*/
VmPeak: 60184 kB /*进程地址空间的大小*/
VmSize: 60180 kB /*进程虚拟地址空间的大小reserved_vm:进程在预留或特殊的内存间的物理页*/
VmLck: 0 kB /*进程已经锁住的物理内存的大小.锁住的物理内存不能交换到硬盘*/
VmHWM: 18020 kB /*文件内存映射和匿名内存映射的大小*/
VmRSS: 18020 kB /*应用程序正在使用的物理内存的大小,就是用ps命令的参数rss的值 (rss)*/
VmData: 12240 kB /*程序数据段的大小(所占虚拟内存的大小),存放初始化了的数据*/
VmStk: 84 kB /*进程在用户态的栈的大小*/
VmExe: 576 kB /*程序所拥有的可执行虚拟内存的大小,代码段,不包括任务使用的库 */
VmLib: 21072 kB /*被映像到任务的虚拟内存空间的库的大小*/
VmPTE: 56 kB /*该进程的所有页表的大小*/
Threads: 1 /*共享使用该信号描述符的任务的个数*/
SigQ: 0/8183 /*待处理信号的个数/目前最大可以处理的信号的个数*/
SigPnd: 0000000000000000 /*屏蔽位,存储了该线程的待处理信号*/
ShdPnd: 0000000000000000 /*屏蔽位,存储了该线程组的待处理信号*/
SigBlk: 0000000000000000 /*存放被阻塞的信号*/
SigIgn: 0000000000001000 /*存放被忽略的信号*/
SigCgt: 0000000180000000 /*存放被俘获到的信号*/
CapInh: 0000000000000000 /*能被当前进程执行的程序的继承的能力*/
CapPrm: 0000000000000000 /*进程能够使用的能力,可以包含CapEff中没有的能力,这些能力是被进程自己临时放弃的*/
CapEff: 0000000000000000 /*是CapPrm的一个子集,进程放弃没有必要的能力有利于提高安全性*/
Cpus_allowed: 01 /*可以执行该进程的CPU掩码集*/
Mems_allowed: 1 
voluntary_ctxt_switches: 1241 /*进程主动切换的次数*/
nonvoluntary_ctxt_switches: 717 /*进程被动切换的次数*/