JAVA22 FFM实战之HelloWorld

前言

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));  //11printfHandler.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 asciijextract `--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_setoptif (!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) doprint(k .. '=' .. v)endprint(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.dlljextract --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.dlljextract --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;}// pcalllua_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);// pcallint 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.mzph.cn/news/748547.shtml

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

相关文章

对模型性能进行评估(Machine Learning 研习十五)

在上一篇我们已然训练了一个用于对数字图像识别的模型,但我们目前还不知道该模型在识别数字图像效率如何?所以,本文将对该模型进行评估。 使用交叉验证衡量准确性 评估模型的一个好方法是使用交叉验证,让我们使用cross_val_score…

【820复试】操作系统面试问题

文章目录 1. 什么是程序局部性,为什么会有程序的空间局部性?2.为了实现重定位需要哪些硬件?3.在交互式系统中,非剥夺是不是一个好的策略?why?4.CPU不执行程序的时候干什么?5.死锁是什么&#xf…

机器学习-04-分类算法-02贝叶斯算法

总结 本系列是机器学习课程的系列课程,主要介绍机器学习中分类算法,本篇为分类算法与贝叶斯算法部分。 本门课程的目标 完成一个特定行业的算法应用全过程: 懂业务会选择合适的算法数据处理算法训练算法调优算法融合 算法评估持续调优工程…

MySQL大小写敏感、MySQL设置字段大小写敏感

文章目录 一、MySQL大小写敏感规则二、设置数据库及表名大小写敏感2.1、查询库名及表名是否大小写敏感2.2、修改库名及表名大小写敏感 三、MySQL列名大小写不敏感四、lower_case_table_name与校对规则4.1、验证校对规则影响大小写敏感4.1、验证校对规则影响排序 五、设置字段内…

Selenium控制已运行的Edge和Chrome浏览器——在线控制 | 人机交互(详细启动步骤和bug记录)

文章目录 前期准备1. 浏览器开启远程控制指令(1)Edge(2)Chrome 2. 执行python代码(1)先启动浏览器后执行代码(2)通过代码启动浏览器(3)Bug问题记录1&#xff…

蓝桥杯决赛2023 RE CyberChef2

思路很清晰,爆IV 但是题目出的有点屎,六位字符串,62的6次方,要我爆到猴年马月? 就当练习脚本吧 #Cyber2 wp from Crypto.Cipher import DES, AES from Crypto.Util.Padding import pad, unpad key_des b0a0b0c0…

PHP异世界云商系统开源源码

系统更新与修复列表 1. 基于彩虹的二次开发 - 对彩虹系统进行了二次开发,增强了系统的功能和性能。2. 新增自定义输入框提示内容(支持批量修改) - 用户可以自定义输入框的提示内容,并支持批量修改,提升用户体验。3. 新…

TCP相关特性

协议段格式 • 源/⽬的端⼝号:表⽰数据是从哪个进程来,到哪个进程去; • 32位序号/32位确认号:后⾯详细讲; • 4位TCP报头⻓度:表⽰该TCP头部有多少个32位bit(有多少个4字节);所以TCP头部最⼤⻓度是15*460 • 6位标志位: ◦ URG:紧急指针是否有效 ◦ ACK:确认号是否有效…

c++高精度减法的方法和示例代码

C中的高精度减法指的是在处理大数时,执行减法操作的方法。通常情况下,C内置的数据类型(如int、long、double等)可能无法满足大数运算的需求,因为它们的范围有限。在这种情况下,需要使用自定义的数据结构或者…

深入理解逻辑运算符:从基础到实战技巧

在 JavaScript 编程中,逻辑运算符是处理条件和控制流程的重要工具。 本文将从基础知识出发,向初学者介绍逻辑运算符的基本用法,并深入探讨工作中常用的一些技巧。 1. 基础知识 逻辑与操作符 && 在两个条件都为真时返回真。 if (…

ARMv8架构特殊寄存器介绍-0

一、zero 寄存器 零寄存器用作源寄存器时读取零,用作目标寄存器时丢弃结果。您可以在大多数指令中使用零寄存器,但不是所有指令。二、sp寄存器 在ARMv8架构中,要使用的堆栈指针的选择在某种程度上与Exception级别。默认情况下,异…

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:ScrollBar)

滚动条组件ScrollBar,用于配合可滚动组件使用,如List、Grid、Scroll。 说明: 该组件从API Version 8开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。 子组件 可以包含单个子组件。 接口 ScrollBar(val…

Linux系统——rsync命令

目录 一、rsync介绍 1.rsync简介 2.rsync同步基本说明 二、rsync用法 1.rsync三种工作方式 2.rsync选项详解 不带任何选项 -t -I(i) -v -z -r -l(L) -p——perserve permissions(保持权限) …

6、Design Script之列表

Range 在DesignScript中,Range是从起点到终点的一系列数字,使用指定的步距(间距类型),并有以下的初始化方法: start..end..step; start..end..#amount; start..end..~approximate; Range可以是数字的,也可以是字母的。 字母范围因大小写而异。 开始,结束. .#数量范围(…

springboot276基于JS的个人云盘管理系统的设计与实现

个人云盘管理系统设计与实现 摘 要 传统办法管理信息首先需要花费的时间比较多,其次数据出错率比较高,而且对错误的数据进行更改也比较困难,最后,检索数据费事费力。因此,在计算机上安装个人云盘管理系统软件来发挥其…

P6技巧:导出XER设置老版本/新版本

前言 在一个大型的项目中,虽然业主方已要求承包商必须使用P6格式来提交计划,但实际情况是承包商会给到你多种不同版本的XER文件,使得得在Oracle Primavera P6 之间导入或导出。 如果收到的 XER 文件不适合你使用的 Primavera P6 版本&#x…

【编程向导】JavaScript-基础语法-解构赋值

梦里繁花落尽,此情未央,此意难忘,弦虽断,曲犹扬。! 与技术共同呼吸,与程序员共命运。世树科技 承“技驱动,理致胜”理念、国风编程。 解构赋值 解构赋值(Destructing)…

java学习之路-程序逻辑控制

目录 1.分支结构 1.1 if语句 栗子 判断奇数还是偶数 判断一个年份是否为闰年 1.2switch语句 栗子 2. 循环结构 2.1while 循环 栗子 2.2break和continue break continue 2.3for循环 基本语法 栗子 2.4 do while 循环 3.输入输出 3.1输出 3.2从键盘输入 栗子…

【Gzip】详细介绍

Gzip讲解 1. 前言2. Gzip压缩算法3. Gzip文件格式4. Gzip使用5. 压缩率与CPU使用 1. 前言 Gzip是一种流行的文件压缩和解压工具,也是一种文件格式。它最早由Jean-loup Gailly和Mark Adler开发,用于UNIX系统。由于高效的压缩算法,它被广泛运用…

南大通用数据库-Gbase-8a-学习-43-SQL长时间处于Writing to net状态排查

目录 一、问题截图 二、排查思路 1、Gbase8a SQL有几种状态 2、问题导致原因猜想 3、观察服务端(集群端)网络情况 4、观察客户端网络情况 5、排查客户端程序处理数据慢 5.1、send (1)声明 (2)作用…