jdk21的外部函数和内存API(MemorySegment)(官方翻译)

简介: 本文介绍了JDK 21中引入的外部函数和内存API(MemorySegment),这些API使得Java程序能够更安全、高效地与JVM外部的代码和数据进行互操作,包括调用外部函数、访问外部内存,以及使用不同的Arena竞技场来分配和管理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));
        }
    }

运行结果:

目录
相关文章
|
1天前
|
IDE API 定位技术
Python--API编程:IP地址翻译成实际的物理地址
Python--API编程:IP地址翻译成实际的物理地址
|
1月前
|
安全 Java API
【性能与安全的双重飞跃】JDK 22外部函数与内存API:JNI的继任者,引领Java新潮流!
【9月更文挑战第7天】JDK 22外部函数与内存API的发布,标志着Java在性能与安全性方面实现了双重飞跃。作为JNI的继任者,这一新特性不仅简化了Java与本地代码的交互过程,还提升了程序的性能和安全性。我们有理由相信,在外部函数与内存API的引领下,Java将开启一个全新的编程时代,为开发者们带来更加高效、更加安全的编程体验。让我们共同期待Java在未来的辉煌成就!
53 11
|
1月前
|
监控 Java 大数据
【Java内存管理新突破】JDK 22:细粒度内存管理API,精准控制每一块内存!
【9月更文挑战第9天】虽然目前JDK 22的确切内容尚未公布,但我们可以根据Java语言的发展趋势和社区的需求,预测细粒度内存管理API可能成为未来Java内存管理领域的新突破。这套API将为开发者提供前所未有的内存控制能力,助力Java应用在更多领域发挥更大作用。我们期待JDK 22的发布,期待Java语言在内存管理领域的持续创新和发展。
|
27天前
|
监控 数据可视化 Java
使用JDK自带的监控工具JConsole来监控线程池的内存使用情况
使用JDK自带的监控工具JConsole来监控线程池的内存使用情况
|
1月前
|
Java API 数据处理
【Java的SIMD革命】JDK 22向量API:释放硬件潜能,让Java应用性能飙升!
【9月更文挑战第7天】 JDK 22向量API的发布标志着Java编程语言在SIMD技术领域的重大突破。这一新特性不仅释放了现代硬件的潜能,更让Java应用性能实现了飙升。我们有理由相信,在未来的发展中,Java将继续引领编程语言的潮流,为开发者们带来更加高效、更加强大的编程体验。让我们共同期待Java在SIMD技术的推动下开启一个全新的性能提升时代!
|
1月前
|
Java API 开发者
【Java字节码的掌控者】JDK 22类文件API:解锁Java深层次的奥秘,赋能开发者无限可能!
【9月更文挑战第8天】JDK 22类文件API的引入,为Java开发者们打开了一扇通往Java字节码操控新世界的大门。通过这个API,我们可以更加深入地理解Java程序的底层行为,实现更加高效、可靠和创新的Java应用。虽然目前它还处于预览版阶段,但我们已经可以预见其在未来Java开发中的重要地位。让我们共同期待Java字节码操控新篇章的到来,并积极探索类文件API带来的无限可能!
|
2月前
|
机器人 API Python
智能对话机器人(通义版)会话接口API使用Quick Start
本文主要演示了如何使用python脚本快速调用智能对话机器人API接口,在参数获取的部分给出了具体的获取位置截图,这部分容易出错,第一次使用务必仔细参考接入参数获取的位置。
131 1
|
18天前
|
安全 API 开发者
Web 开发新风尚!Python RESTful API 设计与实现,让你的接口更懂开发者心!
在当前的Web开发中,Python因能构建高效简洁的RESTful API而备受青睐,大大提升了开发效率和用户体验。本文将介绍RESTful API的基本原则及其在Python中的实现方法。以Flask为例,演示了如何通过不同的HTTP方法(如GET、POST、PUT、DELETE)来创建、读取、更新和删除用户信息。此示例还包括了基本的路由设置及操作,为开发者提供了清晰的API交互指南。
73 6
|
2月前
|
存储 JSON API
淘系API接口(解析返回的json数据)商品详情数据解析助力开发者
——在成长的路上,我们都是同行者。这篇关于商品详情API接口的文章,希望能帮助到您。期待与您继续分享更多API接口的知识,请记得关注Anzexi58哦! 淘宝API接口(如淘宝开放平台提供的API)允许开发者获取淘宝商品的各种信息,包括商品详情。然而,需要注意的是,直接访问淘宝的商品数据API通常需要商家身份或开发者权限,并且需要遵循淘宝的API使用协议。
淘系API接口(解析返回的json数据)商品详情数据解析助力开发者
|
1天前
|
API 数据安全/隐私保护 开发者
淘宝 API:关键词搜商品列表接口,助力商家按价格销量排序分析数据
此接口用于通过关键词搜索淘宝商品列表。首先需在淘宝开放平台注册并创建应用获取API权限,之后利用应用密钥和访问令牌调用接口。请求参数包括关键词、页码、每页数量、排序方式及价格区间等。返回结果含总商品数量及具体商品详情。使用时需注意签名验证及官方文档更新。