jdk21的外部函数和内存API(MemorySegment)(官方翻译)
1、jdk21: 引入一个 API,通过该 API,Java 程序可以与 Java 运行时之外的代码和数据进行互操作。通过有效地调用外部函数(即JVM外部的代码)和安全地访问外部内存(即不由JVM管理的内存),API使Java程序能够调用本机库并处理本机数据,而不会像JNI那样脆弱和危险。这是一个预览版 API。
2、jdk21引入了MemorySegment内存段,和Arena竞技场
3、jdk21为什么要这么做? 答:java开发人员在访问一种重要的非Java资源时依然很困难
4、使用关键字创建的对象存储在 JVM 的堆中,当不再需要时,它们将受到垃圾回收。但是,垃圾收集的成本和不可预测性对于性能关键型库(如Tensorflow,Ignite,Lucene和Netty)来说是不可接受的。他们需要将数据存储在堆外,存储在堆外内存中,这些内存是他们自己分配和解除分配的。对堆外内存的访问还允许通过将文件直接映射到内存中来序列化和反序列化数据,例如 mmap,new
5、java平台历来提供两个用来访问堆外内存的api:
ByteBuffer Api:
提供直接字节缓冲区,这些缓冲区是由固定大小的堆外内存区域支持的 Java 对象。但是,区域的最大大小限制为 2 GB,读取和写入内存的方法基本且容易出错,仅提供对基元值的索引访问。更严重的是,仅当缓冲区对象被垃圾回收时,才会释放支持直接字节缓冲区的内存,这是开发人员无法控制的。缺乏对及时释放的支持使得 API 不适合使用 Java 进行系统编程。ByteBuffer
6、sun.misc.Unsafe Api:
提供对堆内存的低级别访问,该访问也适用于堆外内存。使用速度很快(因为它的内存访问操作是由 JVM 固有的),允许巨大的堆外区域(理论上高达 16 EB),并提供对释放的细粒度控制(因为可以随时调用)。但是,此编程模型很弱,因为它给了开发人员太多的控制。长时间运行的服务器应用程序中的库将随着时间的推移分配堆外内存的多个区域并与之交互;一个区域中的数据将指向另一个区域中的数据,并且必须以正确的顺序释放区域,否则悬而未决的指针将导致释放后使用错误。缺乏对安全释放的支持使得 API 不适合用 Java 进行系统编程。Unsafe
Unsafe::freeMemory
Unsafe
7、总之,复杂的客户端应该得到一个 API,它可以分配、操作和共享堆外内存,具有与堆内存相同的流动性和安全性。这样的 API 应该在可预测的释放需求与防止可能导致 JVM 崩溃或更糟糕的静默内存损坏的不合时宜的释放之间取得平衡。
8、外文函数
8.1、JNI从java1.1开始就支持调用本机函数(外部函数),但是不足点也多
Java API(方法)、派生自 Java API 的 C 头文件以及调用感兴趣的本机库的 C 实现。Java 开发人员必须跨多个工具链工作,以保持依赖于平台的工件同步,这在本机库快速发展时尤其繁重。native
JNI 只能与用语言(通常是 C 和 C++)编写的库进行互操作,这些库使用构建 JVM 的操作系统和 CPU 的调用约定。方法不能用于调用使用不同约定的语言编写的函数。native
JNI 不协调 Java 类型系统与 C 类型系统。Java 中的聚合数据用对象表示,但 C 中的聚合数据用结构表示,因此传递给方法的任何 Java 对象都必须由本机代码费力地解压缩。例如,考虑 Java 中的记录类:将对象传递给方法将需要本机代码使用 JNI 的 C API 从对象中提取字段(例如,和)。因此,Java 开发人员有时会将数据平展为单个对象(例如,字节数组或直接字节缓冲区),但更常见的是,由于通过 JNI 传递 Java 对象很慢,他们使用 API 分配堆外内存并将其地址作为方法传递给方法 - 这使得 Java 代码非常不安全!
9、内存段是由位于堆外或堆上的连续内存区域支持的抽象,分段的时间边界由用于分配分段的竞技场确定。
最简单的竞技场是全球竞技场,它提供了无限的生命周期:它永远活着。
自动竞技场提供有限的生存期:可以访问由自动竞技场分配的段,直到 JVM 的垃圾回收器检测到内存段不可访问,此时支持该段的内存区域被解除分配。
受限竞技场提供有限且确定的生存期:从客户端打开竞技场到客户端关闭竞技场,它一直处于活动状态。在受限竞技场中分配的内存段只能在竞技场关闭之前访问
本人先举个MemorySegment内存段,和Arena竞技场的例子吧
自由竞技场
package org.example.ass;
import java.lang.foreign.*;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.VarHandle;
import static java.lang.foreign.ValueLayout.JAVA_DOUBLE;
class WindowsSharedMemory{
public static void main(String[] args) {
MemorySegment point= Arena.ofAuto().allocate(8*2);
point.set(JAVA_DOUBLE,0,3d);
point.set(JAVA_DOUBLE,8,4d);
System.out.println(point.get(JAVA_DOUBLE, 0));
}
这里我使用的是自由竞技场来创建的,也就是说当这个MemorySegment不可达时,该内存区域被解除分配
这里要注意一点,我是在0号位置set 3d占了8个字节,取的时候,就是在0号位置,取8个字节,这个JAVA_DOUBLE是所占的单位,因为double一般是8个字节
受限竞技场(错误代码),我之前说什么了?在受限竞技场中分配的内存段只能在竞技场关闭之前访问,我故意出个错误的代码
package org.example.ass;
import java.lang.foreign.*;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.VarHandle;
import static java.lang.foreign.ValueLayout.JAVA_DOUBLE;
class WindowsSharedMemory{
public static void main(String[] args) {
MemorySegment point=null;
try ( Arena allocate= Arena.ofConfined()){
point = allocate.allocate(8 * 2);
point.set(JAVA_DOUBLE,0,3d);
point.set(JAVA_DOUBLE,8,4d);
}
System.out.println(point.get(JAVA_DOUBLE, 0));
}
全球竞技场我就不提例子了,因为太简单了,这个竞技场永远存活,把上面的例子中的.ofConfined()改为.global()就好了,运行结果:
没错又是报错,不过你看一下报错信息:试图关闭一个不可关闭的会话
这个是由于try ( 这里面运行完会自动调用关闭的那个方法){} 造成的,我就是故意举出错误例子,让你们深刻理解
10、为了应对c语言的结构体,例如:
Point
Point
Point.x
Point.y
struct Point {
int x;
int y;
} pts[10];
那我们如何在java的内存段里面搞呢?
openjdk引入了一个类,叫内存布局Memorylayout
package org.example.ass;
import java.lang.foreign.*;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.VarHandle;
import static java.lang.foreign.MemoryLayout.sequenceLayout;
import static java.lang.foreign.ValueLayout.JAVA_DOUBLE;
class WindowsSharedMemory{
public static void main(String[] args) {
WindowsSharedMemory windowsSharedMemory=new WindowsSharedMemory();
windowsSharedMemory.play();
}
public void play(){
SequenceLayout POINT_2D =sequenceLayout(10, MemoryLayout.structLayout(
JAVA_DOUBLE.withName("x"),
JAVA_DOUBLE.withName("y")
));
VarHandle xHandle // (MemorySegment, long) -> int
= POINT_2D.varHandle(MemoryLayout.PathElement.sequenceElement(),
MemoryLayout.PathElement.groupElement("x"));
VarHandle yHandle // (MemorySegment, long) -> int
= POINT_2D.varHandle(MemoryLayout.PathElement.sequenceElement(),
MemoryLayout.PathElement.groupElement("y"));
MemorySegment segment = Arena.ofAuto().allocate(POINT_2D);
for (int i = 0; i < POINT_2D.elementCount(); i++) {
xHandle.set(segment,
/* index */ (long) i,
/* value to write */ i); // x
yHandle.set(segment,
/* index */ (long) i,
/* value to write */ i); // y
}
for (int i = 0; i < POINT_2D.elementCount(); i++) {
System.out.println(xHandle.get(segment,
/* index */ (long) i)); // x
System.out.println(yHandle.get(segment,
/* index */ (long) i)); // y
}
}
运行结果:
区段分配器
当客户端使用堆外内存时,内存分配通常是一个瓶颈。因此,FFM API 包括一个分段分配器抽象,用于定义分配和初始化内存段的操作。为方便起见,Arena 类实现了该接口,以便可以使用 arenas 来分配本机段。换句话说,是灵活分配和及时释放堆外内存的“一站式商店”:SegmentAllocator
Arena
package org.example.ass;
import java.lang.foreign.*;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.VarHandle;
import static java.lang.foreign.MemoryLayout.sequenceLayout;
import static java.lang.foreign.ValueLayout.JAVA_DOUBLE;
import static java.lang.foreign.ValueLayout.JAVA_INT;
class WindowsSharedMemory{
public static void main(String[] args) {
try (Arena offHeap = Arena.ofConfined()) {
MemorySegment nativeArray = offHeap.allocateArray(ValueLayout.JAVA_INT,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
MemorySegment nativeString = offHeap.allocateUtf8String("Hello!");
System.out.println(nativeArray.get(JAVA_INT, 0));
System.out.println(nativeArray.get(JAVA_INT, 4));
System.out.println(nativeArray.get(JAVA_INT, 8));
System.out.println(nativeArray.get(JAVA_INT, 12));
System.out.println(nativeString.getUtf8String(0));
}
}
运行结果:
jdk21的外部函数和内存API(MemorySegment)(官方翻译)的更多相关文章
- JNI的又一替代者—使用JNR访问Java外部函数接口(jnr-ffi)
1. JNR简单介绍 继上文“JNI的替代者—使用JNA访问Java外部函数接口”,我们知道JNI越来越不受欢迎,JNI是编写Java本地方法以及将Java虚拟机嵌入本地应用程序的标准编程接口.它管理 ...
- 深入理解类成员函数的调用规则(理解成员函数的内存为什么不会反映在sizeof运算符上、类的静态绑定与动态绑定、虚函数表)
本文转载自:http://blog.51cto.com/9291927/2148695 总结: 一.成员函数的内存为什么不会反映在sizeof运算符上? 成员函数可以被看作是类 ...
- 外部函数/external
定义在moodle/lib/externallib.php 概观 外部函数API允许您创建可由外部程序(如Web服务API)访问的完全参数化的方法. 外部函数位于externallib.php文件中. ...
- 寄存器变量 extern 外部变量 外部函数
寄存器变量 这个可以不理睬 register 关键字定义的变量直接放在寄存器当中 寄存器是放在CPU内部的存储单元,它的速度比内存快的多,所以当程序中有10000多次调用同一个变量的时候声明成寄存器变 ...
- LoadRunner如何调用外部函数
LoadRunner如何调用外部函数 使用 VuGen 时,可以调用在外部 DLL 中定义的函数.通过从脚本调用外部函数,可以降低脚本的内存使用量以及总体运行时间.要调用外部函数,需要加载定义了该函数 ...
- 关于js调用外部部署的web api
没想到多年之后我还得继续写这些东西.... 瀑布汗~ 最近不得不开始研究用web api MVC的项目中,在js文件里,实现点击一个按钮调用外部发布好的api,再从api把值回传给js页面,跳转. 经 ...
- 【C语言】外部函数和内部函数
目录 [外部函数] [内部函数] 1.外部函数 定义的函数能被本文件和其它文件访问(默认). 注:不允许有同名的外部函数. 2.内部函数 定义的函数只能被本文件访问,其它文件不能访问. 注:允许 ...
- 如何在 ejs 模板中使用 helper function 外部函数进行特殊处理?
一般我们想要在 ejs 模板中使用外部函数用于特殊的处理,比如:<%= ellipsis(title, 30) %> 通常的做法是: 使用 app.locals 来定义: app.loca ...
- 使用函数库(JAVA API)
/*使用函数库(JAVA API) * 在JAVA的API里类被封装在一个个的package,要使用package的类之前必须 * 要知道这个类属于哪个package * 引用类方式: * 1.通过i ...
- Jmeter外部函数引用
Jmeter外部函数引用 1.Beanshell引用Jmeter变量 添加用户自定义变量,输入变量名称和变量值,添加Debug sampler,用于输出初始变量值.
随机推荐
- Callback Function Essence
Include Example Input: I am a. route execute finish. I am b. route execute finish. What is Callback ...
- 12、Spring之基于xml的AOP
阅读本文前,建议先阅读Spring之基于注解的AOP 12.1.环境搭建 创建名为spring_aop_xml的新module,过程参考9.1节 12.1.1.配置打包方式和依赖 <?xml v ...
- 安装iTerm2和oh-my-zsh
安装iTerm2和oh-my-zsh 此文是在参考许多教程(见目录:参考)并结合本人安装经历写下的一篇关于iTerm2和oh-my-zsh的认识和超级详细安装教程.全文所有图片均为本人截屏拍摄.希望能 ...
- 【page cache】简介
目录 page cache 直接 IO 与 缓存 IO Linux IO 栈 Linux 中的具体实现 相关结构体 超级块 super_block 索引节点 inode 文件 file 目录项 den ...
- 一文解锁vue3中hooks的使用姿势
vue3 中的 hooks 是什么? 简单来说如果你的函数中用到了诸如 ref,reactive,onMounted 等 vue 提供的 api 的话,那么它就是一个 hooks 函数,如果没用到它就 ...
- Java实践项目 - 购物车模块
Smiling & Weeping ----世界上美好的东西不太多,立秋傍晚从河对岸吹来的风, 加入购物车 1.数据创建--创建t_cart CREATE TABLE t_cart( cid ...
- 【krpano】KRPano测试开发专用浏览器
KRPano技术解密网站:http://www.krpano.tech 在开发KRPano项目时候,每次测试时均需要打开测试服务器,或者上传到空间中才能进行测试,十分不方便. 因此本站提供了免费的KR ...
- Sentinel系列之SlotChain、NodeSelectorSlot、ClusterBuilderSlot分析
本文基于Sentinel 1.8.6版本分析 1. SlotChain 我们从入口com.alibaba.csp.sentinel.SphU#entry(java.lang.String) 开始分析. ...
- Vue源码学习(九):响应式前置:实现对象的依赖收集(dep和watcher)
好家伙,这是目前为止最绕的一章,也是十分抽象的一章 由于实在太过抽象,我只能用一个不那么抽象的实例去说服我自己 完整代码已开源https://github.com/Fattiger4399/ana ...
- 【第一章 web入门】afr_3——模板注入与proc文件夹
[第一章 web入门]afr_3--模板注入与proc文件夹 题目来源n1book,buu上的环境 看题 url中提供了name参数,类似在路径中进行了文件名查询然后展示: 随便输入一个数字: 说明肯 ...