JAVA22 FFM实战之HelloWorld

news2025/3/12 23:15:38

前言

JDK22即将发布,Java Foreign Function & Memory API将会退出预览,是时候开始学习一波了。

FFM API介绍

FFM API由两大部分组成,一个是Foreign Function Interface,另一个是Memory API。前者是外部函数接口,简称FFI,用它来实现Java代码和外部代码之间相互操作;后者是内存接口,用于安全地管理堆外内存。

上面说这么多,可以简单的认为是jni的替代品。

本文不关注底层如何实现,一两个常用的c库为例子,重点在了解api的使用以及使用jextract生成java代码

入门

使用c语言的strlen和printf格式化打印helloworld

/**
 * @author authorZhao
 * @since 2024-03-13
 */
public class FFMHelloWorld {
    public static void main(String[] args) {
        Linker linker = Linker.nativeLinker();
        SymbolLookup defaultLookup = linker.defaultLookup();
        MethodHandle strlenHandle = linker.downcallHandle(
                defaultLookup.find("strlen").orElseThrow(),
                FunctionDescriptor.of(JAVA_LONG, ADDRESS));
        MethodHandle printfHandler = linker.downcallHandle(
                defaultLookup.find("printf").orElseThrow(),
                FunctionDescriptor.of(JAVA_LONG, ADDRESS,JAVA_INT));
        try (Arena offHeap = Arena.ofConfined()) {
            MemorySegment pointers = offHeap.allocateUtf8String("Hello world%d!");
            System.out.println(strlenHandle.invoke(pointers));  //11
            printfHandler.invoke(pointers,8);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }
}

实战级别的HelloWorld

工具准备

下载curl

下载lua

  • 注意需要下载源码最好,有条件的自行编译,编译为动态库

  • 效果如图,本文只需要dll即可没动态库名字我自己改的

  • 头文件目录

在这里插入图片描述
在这里插入图片描述
这里的cjson已经被移动到同级目录的dll目录下面

下载jextract 本文选择jdk22的版本

打开jextract的github链接

一、CURL

打开curl的例子 https://github.com/openjdk/jextract/tree/master/samples/libcurl

windows脚本如下

param(
  [Parameter(Mandatory=$true, HelpMessage="The path to the lib curl installation")]
  [string]$curlpath
)

jextract `
  -I "$curlpath\include" `
  -I "$curlpath\include\curl" `
  --dump-includes 'includes_all.conf' `
  "$curlpath\include\curl\curl.h"
  
Select-String -Path 'includes_all.conf' -Pattern '(curl|sockaddr )' | %{ $_.Line } | Out-File -FilePath 'includes_filtered.conf' -Encoding ascii

jextract `
  --output src `
  -t org.jextract `
  -I "$curlpath\include" `
  -I "$curlpath\include\curl" `
  -llibcurl `
  '@includes_filtered.conf' `
  "$curlpath\include\curl\curl.h"

javac -d classes (ls -r src/*.java)

本人以自己的curl为例

生成脚本

本人的curlpath = E:/tool/curl-8.6.0_6-win64-mingw/curl-8.6.0_6-win64-mingw

我按照github执行了两次命令

1. 这个执行完毕得到一个includes_filtered.conf文件
jextract --output src -t org.jextract -I "E:/tool/curl-8.6.0_6-win64-mingw/curl-8.6.0_6-win64-mingw/include" -I "E:/tool/curl-8.6.0_6-win64-mingw/curl-8.6.0_6-win64-mingw/include/curl" -llibcurl '@includes_filtered.conf' "E:/tool/curl-8.6.0_6-win64-mingw/curl-8.6.0_6-win64-mingw/include/curl/curl.h"

2.这个执行完毕直接得到生成的java代
jextract --output src -t org.jextract -I "E:/tool/curl-8.6.0_6-win64-mingw/curl-8.6.0_6-win64-mingw/include" -I "E:/tool/curl-8.6.0_6-win64-mingw/curl-8.6.0_6-win64-mingw/include/curl" "E:/tool/curl-8.6.0_6-win64-mingw/curl-8.6.0_6-win64-mingw/include/curl/curl.h"

拷贝生成java代码(好家伙52M)到idea

生成的工具有一个方法和我本地jdk有点不一致(可能是版本原因,本地jdk21,工具依赖的jdk是22), ctrl+shift+r 全局替换一下getUtf8String

arena.allocateFrom(urlStr); 替换为  arena.getUtf8String(urlStr);

启动

import java.lang.foreign.Arena;

import static java.lang.foreign.MemorySegment.NULL;
import static org.jextract.curl_h.*;

public class CurlMain {
    static {
        System.load("E:\\tool\\curl-8.6.0_6-win64-mingw\\curl-8.6.0_6-win64-mingw\\bin\\libcurl-x64.dll");
    }
   public static void main(String[] args) {
       var urlStr = "https://www.baidu.com";
       curl_global_init(CURL_GLOBAL_DEFAULT());
       var curl = curl_easy_init();
       //curl_easy_setopt
       if (!curl.equals(NULL)) {
           try (var arena = Arena.ofConfined()) {
               var url = arena.allocateUtf8String(urlStr);
               //忽略ssl忽略证书检查
               curl_easy_setopt.makeInvoker(C_LONG_LONG).apply(curl, CURLOPT_SSL_VERIFYPEER(), 0);
               curl_easy_setopt.makeInvoker(C_LONG_LONG).apply(curl, CURLOPT_URL(), url.address());
               int res = curl_easy_perform(curl);
               if (res != CURLE_OK()) {
                   String error = curl_easy_strerror(res).getUtf8String(0);
                   System.out.println("Curl error: " + error);
                   curl_easy_cleanup(curl);
               }
           }
       }
       curl_global_cleanup();
   }
}

curl的java执行结果

在这里插入图片描述

二、LUA

lua源代码

print("package.cpath=" .. package.cpath)
-- 注意cjson不在lua.exe同级目录,再下一级别
package.cpath = package.cpath .. ';E:/tool/lua5.4-zhao/dll/?.dll'
local cjson = require("cjson")

function test()
    local map = {}
    map["a"] = "张三"
    map["b"] = "李四"
    for k,v in pairs(map) do
        print(k .. '=' .. v)
    end
    print(cjson.encode(map))
    return 100
end
test()

lua源代码的直接执行结果

在这里插入图片描述

生成命令

生成代码并,拷贝代码到idea

jextract --output src -t com.lua -I "E:/tool/lua-5.4.4/src" "E:/tool/lua-5.4.4/src/lua.h" -l :E:/tool/lua5.4-zhao/liblua5.4.0.dll

jextract --output src -t com.lua -I "E:/tool/lua-5.4.4/src" "E:/tool/lua-5.4.4/src/lualib.h" -l :E:/tool/lua5.4-zhao/liblua5.4.0.dll

jextract --output src -t com.lua -I "E:/tool/lua-5.4.4/src" "E:/tool/lua-5.4.4/src/lauxlib.h" -l :E:/tool/lua5.4-zhao/liblua5.4.0.dll

在这里插入图片描述

idea导入生成的java代码

/**
 * @author authorZhao
 */
public class LuaMain {
    static {
        //这里不适用load,生成代码使用-l指定动态库的位置,生成的代码直接包含了,但是这种方式用的是绝对路径,使用SymbolLookup.libraryLookup
        //System.load("E:/tool/lua5.4-zhao/liblua5.4.0.dll");
    }

    public static void main(String[] args) {
        var lua_State = luaL_newstate();
        if (lua_State == NULL) {
            return;
        }
        //打开内置库
        luaL_openlibs(lua_State);
        try (var arena = Arena.ofConfined()) {
            var code = arena.allocateUtf8String("print(\"渣渣辉\") return \"嘿嘿\"");
            //加载而不执行
            int i = luaL_loadstring(lua_State, code);
            if (i != 0) {
                return;
            }
            // pcall
            lua_h.lua_pcallk(lua_State, 0, 1, 0, 0L, NULL);
            var data2 = lua_h.lua_tolstring(lua_State, -1,NULL);
            System.out.println("data2 = " + data2.getUtf8String(0L));

            //这里的utf-8可能对中文路径有问题,英文影响应该不大
            int loadResult = luaL_loadfilex(lua_State, arena.allocateUtf8String("E:/tool/lua5.4-zhao/test2.lua"), NULL);
            System.out.println("loadResult = " + loadResult);

            // pcall
            int iRet = lua_h.lua_pcallk(lua_State, 0, 1, 0, 0L, NULL);

            if (iRet==0){
                var data = lua_h.lua_tointegerx(lua_State, -1,NULL);
                System.out.println("data = " + data);
            }

        }
        //关闭lua虚拟机
        lua_h.lua_close(lua_State);
    }

}

执行结果

在这里插入图片描述

本文运行环境

本文系统win10 运行jdk21,jextract工具是jdk22的版本,lua5.4

使用总结

  • curl本身就3M生成的代码文件有55M,编译的class有2万多个(可能是姿势不对,跟多类都没用到)
  • 体验还行,但是如果不了解这个c库,可能很多api都不会用,例如图上的ssl证书,不处理https都报错,lua的capi
  • 该工具目前无法识别c++的语法,以qt为例目前使用失败(有可能是使用姿势不对),理论上是要支持的
  • 以lua为例,一些宏无法识别
  • lua里面使用别的dll或者so,搜索路径是jdk下面开头的,这个需要注意
  • System.load是以前的jni加载动态库的方式,在这里照样可以这么用,网上查了一下SymbolLookup.libraryLookup会忽略jni_onload等函数
  • 生成代码的时候如果使用绝对路径指定动态库的位置,可能会造成跨平台问题,这里需要注意

参考

jextract的github地址 https://github.com/openjdk/jextract

lua5.4文档 https://www.lua.org/manual/5.4/

lua5.3中文文档(这里面对capi翻译的很详细) https://www.runoob.com/manual/lua53doc/manual.html#lua_tolstring

openjdk的jextract下载地址 https://jdk.java.net/jextract/

本文为原创,转载请申明

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1520861.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构 之 队列(Queue)

​​​​​​​ 🎉欢迎大家观看AUGENSTERN_dc的文章(o゜▽゜)o☆✨✨ 🎉感谢各位读者在百忙之中抽出时间来垂阅我的文章,我会尽我所能向的大家分享我的知识和经验📖 🎉希望我们在一篇篇的文章中能够共同进步&#xff0…

双指针算法_移动零_

题目: 给定一个数组 num ,编写一个函数将数组内部的数字0都移动到数组的末尾,同时保持非零元素的相对顺序! 同时不能通过复制数组,开辟新的数组空间的情况下原地对数组进行操作 示例: 本题的原理&#x…

掘根宝典之C++普通迭代器和反向迭代器详解

简介 迭代器是一种用于遍历容器元素的对象。它提供了一种统一的访问方式,使程序员可以对容器中的元素进行逐个访问和操作,而不需要了解容器的内部实现细节。 C标准库里每个容器都定义了迭代器,这迭代器的名字就叫容器迭代器 迭代器的作用类…

谷歌浏览器运行vue项目。 控制台打印cjs.js,如何解决

当浏览器运行vue项目。 控制台打印cjs.js,如何解决 :

oracle基础-子查询 备份

一、什么是子查询 子查询是在SQL语句内的另外一条select语句,也被称为内查询活着内select语句。在select、insert、update、delete命令中允许是一个表达式的地方都可以包含子查询,子查询也可以包含在另一个子查询中。 【例1.1】在Scott模式下&#xff0…

Java项目企业设备管理系统

java项目企业设备管理系统javaweb项目ssm框架项目 运行环境:idea/eclipse tomcat jdk mysql navicat 系统用户分为员工和管理员两类用户。两类用户都可以进行系统的登录,虽然进入的系统主页结构相似,但是在功能上有不同。员工的密码可以自己进入系统后…

DirectShowPlayerService::doSetUrlSource: Unresolved error code 0x800c000d

报出这个问题,应该是对给的url解析不正确,我给的是rtsp的视频流地址,应该是对该格式解析异常。 所以参考两篇文: QT无法播放视频:报错:DirectShowPlayerService::doRender: Unresolved error code 0x8004…

2024年3月16日云仓酒庄广西发布会圆满举行

原标题:云仓酒庄广西发布会圆满举行,致敬经销商团队共谋未来发展 2024年3月16日,备受瞩目的云仓酒庄广西发布会在广西南宁隆重举行。此次发布会旨在感谢广西地区经销商团队的长期支持,并共同推进未来发展蓝图。活动现场氛围热烈&…

总要有一次,为自己疯狂

机会其实不多 最近一口气看了《飞驰人生》以及《飞驰人生2》,过去是以看喜剧的心态去看沈腾的电影,当如今二刷时发现这不就是生活吗,只不过用喜剧的外壳做了层包装。两部电影给我影响最深的就是最后的那段对白,“张弛&#xff0c…

【大模型系列】统一图文理解与生成(BLIP/BLIPv2/InstructBLIP)

文章目录 1 BLIP(2022, Salesforce Research)1.1 简介1.2 数据角度1.3 模型角度1.4 BLIP预训练的目标 2 BLIP2(ICML2023, Salesforce)2.1 简介2.2 模型架构2.3 训练细节 3 InstructBLIP(2023, Salesforce)3.1 指令微调技术(Instruction-tuning)3.2 数据集准备3.3 Instruction-a…

数据结构——动态顺序表

数据结构的动态顺序表有以下几个操作:创建,销毁,初始化,增删查改和打印以及内存空间不够时的扩容 本文的宏定义: #define SeqTypeData int 1.动态顺序表的创建 typedef struct SeqListInit{//动态顺序表的创建SeqT…

双指针算法_复写零

题目: 给一个固定长度的数组arr,将数组中出现的每一个0都复写一遍,并且将其余元素都往右移动 且不要再超过数组长度的位置写入元素,在数组上直接修改 示例: 双数组模拟操作: 从示例来看,因为…

【状态压缩DP】第十三届蓝桥杯省赛C++ B组《积木画》(C++)

【题目描述】 小明最近迷上了积木画,有这么两种类型的积木,分别为 I 型(大小为 2 个单位面积)和 L 型(大小为 3 个单位面积): 同时,小明有一块面积大小为 2N 的画布,画布…

模板进阶:非类型模板参数,特化

一、非类型模板参数 非类型模板参数&#xff0c;就是用一个常量作为 类/函数 的模板参数&#xff0c;在 类/函数 中可以被当成常量使用。 template<class T, size_t N>// N 为一个非类型模板参数 class Stack { public:Stack(){_a new T[N];} protected:T* _a; };int m…

腾讯云服务器入站规则端口开放使用指南(CentOS系统)

第一步&#xff1a;开放安全组入站规则 来源处0.0.0.0/0是对IPv4开发&#xff0c;::/0是对IPv6开放&#xff1b; 协议端口按照提示填写即可。云服务器防火墙开放 第三步&#xff1a;本地防火墙开放 sudo firewall-cmd --zonepublic --add-port你的端口号/tcp --perma…

RTT——stm32f103的can总线通信

1.创建工程 2.配置时钟和引脚 引脚配置使能CAN 时钟配置&#xff0c;采用外部高速时钟 生成MDK工程后复制相关初始化函数到RTT-studio中 将void HAL_CAN_MspInit(CAN_HandleTypeDef* canHandle)函数复制至broad.c文件中 将时钟配置函数复制到drv_clk.c中&#xff0c;只复制函数…

Java8中Stream流API最佳实践Lambda表达式使用示例

文章目录 一、创建流二、中间操作和收集操作筛选 filter去重distinct截取跳过映射合并多个流是否匹配任一元素&#xff1a;anyMatch是否匹配所有元素&#xff1a;allMatch是否未匹配所有元素&#xff1a;noneMatch获取任一元素findAny获取第一个元素findFirst归约数值流的使用中…

Linux下的多线程编程:原理、工具及应用(2)

&#x1f3ac;慕斯主页&#xff1a;修仙—别有洞天 ♈️今日夜电波&#xff1a;Flower of Life—陽花 0:34━━━━━━️&#x1f49f;──────── 4:46 &#x1f504; ◀️ ⏸ ▶️ ☰ …

【C++ RB树】

文章目录 红黑树红黑树的概念红黑树的性质红黑树节点的定义红黑树的插入代码实现总结 红黑树 AVL树是一颗绝对平衡的二叉搜索树&#xff0c;要求每个节点的左右高度差的绝对值不超过1&#xff0c;这样保证查询时的高效时间复杂度O( l o g 2 N ) log_2 N) log2​N)&#xff0c;…

钉钉小程序 - - - - - 如何通过一个链接打开小程序内的指定页面

方式1 钉钉小程序 scheme dingtalk://dingtalkclient/action/open_mini_app?miniAppId123&pagepages%2Findex%2Findex%3Fx%3D%25E4%25B8%25AD%25E6%2596%2587 方式2 https://applink.dingtalk.com/action/open_mini_app?type2&miniAppIdminiAppId&corpIdcorpId&…