Skip to content

Commit

Permalink
udpate jnr and jna
Browse files Browse the repository at this point in the history
  • Loading branch information
xliuqq committed Sep 20, 2024
1 parent c082172 commit d358128
Show file tree
Hide file tree
Showing 4 changed files with 186 additions and 10 deletions.
72 changes: 71 additions & 1 deletion docs/languages/java/native/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
>
> - JNI, JavaCPP.


## JNI(C)

标准的 Java native通信工具,但复杂度高,需要很多**native code到java的回调**
Expand All @@ -26,10 +28,71 @@

> https://github.com/java-native-access/jna
- **基于[libffi](../../libffi.md);基于反射,数据转换从本地代码跳到 Java 代码非常昂贵;**
- 不需要 JNI 代码 ,只需要定义接口和函数(匹配);
- 相较于JNR,更为成熟;

### 原理

> - 基于反射,数据转换从本地代码跳到 Java 代码非常昂贵;
> - 基于[libffi](../../libffi.md) 进行函数调用约定;
`Native.loadLibrary("c", LibC.class)` 获取了接口实现

- 通过**动态代理(dynamic proxy)**实现

- 根据返回参数的不同,分派到 Native 类的,invokeXxx 本地方法:

```java
/**
* Call the native function.
*
* @param function Present to prevent the GC to collect the Function object
* prematurely
* @param fp function pointer
* @param callFlags calling convention to be used
* @param args Arguments to pass to the native function
*
* @return The value returned by the target native function
*/
static native int invokeInt(Function function, long fp, int callFlags, Object[] args);

static native long invokeLong(Function function, long fp, int callFlags, Object[] args);

static native Object invokeObject(Function function, long fp, int callFlags, Object[] args);
...
```

`invokeXxx` 本地方法的实现 [dispatch.c#L2122](https://github.com/java-native-access/jna/blob/4.5.X/native/dispatch.c#L2122)`invokeInt``invokeLong` 实现源码类似):

```c
/*
* Class: com_sun_jna_Native
* Method: invokeInt
* Signature: (Lcom/sun/jna/Function;JI[Ljava/lang/Object;)I
*/
JNIEXPORT jint JNICALL
Java_com_sun_jna_Native_invokeInt(JNIEnv *env, jclass UNUSED(cls),
jobject UNUSED(function), jlong fp, jint callconv,
jobjectArray arr)
{
ffi_arg result;
dispatch(env, L2A(fp), callconv, arr, &ffi_type_sint32, &result);
return (jint)result;
}
```
即,全部 `invokeXxx` 本地方法统一被分派到 `dispatch` 函数 [dispatch.c#L439](https://github.com/java-native-access/jna/blob/4.5.X/native/dispatch.c#L439):
```c
static void
dispatch(JNIEnv *env, void* func, jint flags, jobjectArray args,
ffi_type *return_type, void *presult)
```

`dispatch` 函数是,需要调用的函数指针地址、输入参数和返回参数,全部是**运行时确定**。要想完成这个函数调用逻辑,就要运行时构造栈帧,生成参数压栈和清理堆栈的工作。JNA 3.0 之前,实现运行时构造栈帧的逻辑的对应代码 [dispatch_i386.c](https://github.com/java-native-access/jna/blob/2.5/jnalib/native/dispatch_i386.c)[dispatch_ppc.c](https://github.com/java-native-access/jna/blob/2.5/jnalib/native/dispatch_ppc.c)[dispatch_sparc.s](https://github.com/java-native-access/jna/blob/2.5/jnalib/native/dispatch_sparc.s),分别实现 Intel x86、PowerPC 和 Sparc 三种 CPU 架构。

[JNA 3.0](https://github.com/java-native-access/jna/tree/3.0/jnalib/native) 开始,摒弃了原先手动构造栈帧的做法,把 libffi 集成进了 JNA。



## JNR(C)
Expand All @@ -41,7 +104,14 @@

<img src="pics/jnr.png" alt="jnr" style="zoom:33%;" />

### 原理

JNA 使用动态代理生成实现类,而 JNR 使用 ASM 字节码操作库生成直接实现类,去除了每次调用本地方法时额外的动态代理的逻辑。

- 使用 ASM 生成实现类,对应的代码为 [AsmLibraryLoader.java](https://github.com/jnr/jnr-ffi/blob/master/src/main/java/jnr/ffi/provider/jffi/AsmLibraryLoader.java)
- 还是需要使用 JNI 去调用 libffi 的代码;

但其调用栈,相对于直接使用 JNI,还是比较繁琐。

## Project Panama(C)

Expand Down
23 changes: 21 additions & 2 deletions docs/languages/java/native/jni.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,25 @@ jobject NewObjectV(jclass clazz, jmethodID methodID, va_list args)

### 二、方法操作

```c
// 查找Test类
jclass testClass = env->FindClass("com/afei/jnidemo/Test");
// 获取Test类的构造函数ID(无参构造函数)
jmethodID constructorID = env->GetMethodID(testClass, "<init>", "()V");
// 创建Test类的实例
jobject testInstance = env->NewObject(testClass, constructorID);
// 获取show方法的ID
jmethodID showMethodID = env->GetMethodID(testClass, "show", "(Ljava/lang/String;I)I");
// 创建一个jstring对象
jstring jMsg = env->NewStringUTF("Hello from JNI!");
// 调用show方法
jint result = env->CallIntMethod(testInstance, showMethodID, jMsg, 42);
```
#### 1. jmethodID GetMethodID(jclass clazz, const char name, const char sig)
说明:获取类中某个非静态方法的ID
Expand Down Expand Up @@ -202,6 +221,7 @@ JNI 调用如下:
jclass clazz = env->FindClass("com/afei/jnidemo/Test");
jmethodID constructor_method = env->GetMethodID(clazz, "<init>", "()V");
jmethodID show_method=env->GetMethodID(clazz,"show", "(Ljava/lang/String;I)I");

```
签名时其中括号内是方法的参数,括号后是返回值类型。例如 show 方法,第一个参数是 String 类,对应 `Ljava/lang/String;`(注意后面有一个分号),第二个参数是 int 基本类型,对应的类型描述符是 `I`,返回值也是 int,同样是 `I`,所以最终该方法的签名为 `(Ljava/lang/String;I)I`。
Expand Down Expand Up @@ -437,7 +457,7 @@ JNI 支持3中不透明的引用:**局部(local)引用**、**全局(global)引



局部引用只有当**本地函数返回Java(当Java调用native)****调用线程detach JVM(native调用Java)**时才会被GC,因此,当有**长时间运行的本地函数**或者**创建很多局部引用**时需要调用**DeleteLocalRef**进行引用删除。
局部引用默认只有当**本地函数返回Java(当Java调用native)****调用线程detach JVM(native调用Java)**时才会被GC,因此,当有**长时间运行的本地函数**或者**创建很多局部引用**时需要调用**DeleteLocalRef**进行引用删除。

<font color='red'>对于C++ call Java时创建的global reference和local reference 创建,需要定义其释放之处;</font>

Expand Down Expand Up @@ -491,4 +511,3 @@ After an exception has been raised, the native code must first clear the excepti

- DeleteWeakGlobalRef、MonitorExit、PushLocalFrame、PopLocalFrame


70 changes: 65 additions & 5 deletions docs/languages/java/native/jni_demo.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
gcc -shared -fPIC -D_REENTRANT -I${JAVA_HOME}/include/linux -I${JAVA_HOME}/include -I/home/test/jnidemo/ JNIDemo.c -o libJNIDemo.so
```


**关于C代码中的jobject obj 的解释:**

- 如果native方法不是static的话,这个obj就代表这个native方法的**类实例**
Expand All @@ -35,8 +34,26 @@
- 需要指定jni.h的头文件路径,通过 `-I` 选项加在 gcc/g++ 编译时;

- 通过`-Lpath -ljvm`,运行时还需要加载链接库路径;

-`$JAVA_HOME/jre/lib/amd64/server`路径添加到PATH(可以搜索到链接库的地址)

```c++
#include<jni.h>
#include <stdio.h>

// 创建 VM 虚拟机
JNIEnv* create_vm(JavaVM** jvm, JNIEnv** env)
{
JavaVMInitArgs args;
JavaVMOption options[1];
args.version = JNI_VERSION_1_6;
args.nOptions = 1;
options[0].optionString = "-Djava.class.path=./";
args.options = options;
args.ignoreUnrecognized = JNI_FALSE;
return JNI_CreateJavaVM(jvm, (void **)env, &args);
}

char* jstringtochar(JNIEnv *env, jstring jstr ) {
char* rtn = NULL;
Expand Down Expand Up @@ -77,12 +94,55 @@
return newArr;

}


int main(int argc, char **argv) {
JavaVM* jvm;
JNIEnv* env;

jclass cls;
int ret = 0;

jmethodID mid;

/* 1. create java virtual machine */
if(create_vm(&jvm, &env)) {
printf("can not create jvm\n");
return -1;
}

/* 2. get class */
cls = (*env)->FindClass(env, "Hello");
if(cls == NULL) {
printf("can not find hello class\n");
ret = -1;
goto destory;
}

/* 3. create object */

/* 4. call method
* 4.1 get method
* 4.2 create parameter
* 4.3 call method
*/

mid = (*env)->GetStaticMethodID(env, cls, "main", "([Ljava/lang/String;)V");
if(mid == NULL) {
ret = -1;
printf("can not get method\n");
goto destory;
}

(*env)->CallStaticVoidMethod(env, cls, mid, NULL);

destory:
(*jvm)->DestroyJavaVM(jvm);

return ret;
}
```
- 通过`-Lpath -ljvm`,运行时还需要加载链接库路径;
- 将`$JAVA_HOME/jre/lib/amd64/server`路径添加到PATH(可以搜索到链接库的地址)
## 注意
Expand Down
31 changes: 29 additions & 2 deletions docs/scheduler_system/yarn/graceful_decommission.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,31 @@
# 优雅退出
# Yarn节点上下线

> https://hadoop.apache.org/docs/stable/hadoop-yarn/hadoop-yarn-site/GracefulDecommission.html
> 官方文档:https://hadoop.apache.org/docs/stable/hadoop-yarn/hadoop-yarn-site/GracefulDecommission.html
## 概述

YARN 很容易扩展:任何新的 NodeManager 都可以加入配置的 ResourceManager 并开始执行作业。。但为了实现完全弹性,我们需要一个停用过程,这有助于删除现有节点并缩小集群规模。

节点停用有两种策略:

- `Normal Decommission` :立即关闭
- `GRACEFUL Decommission `:不会在其上调度新容器并等待正在运行的容器完成(或超时),再将节点转换为 DECOMMISSIONED



## Normal Decommission

步骤:(不需要重启 ResourceManager)

- 将属性`yarn.resourcemanager.nodes.exclude-path` 添加到 `yarn-site.xml`

- 创建一个文本文件(位置在上一步中定义),其中一行包含所选 NodeManager 的名称
- 执行命令:`./bin/yarn rmadmin -refreshNode`



## GRACEFUL Decommission

> 默认 client 端操作,阻塞。
`Normal Decommission`, 只是需要配置 `-g timeout` 参数。

0 comments on commit d358128

Please sign in to comment.