Module java.base

Interface SymbolLookup

功能接口:
这是一个功能接口,因此可以用作lambda表达式或方法引用的赋值目标。

@FunctionalInterface public interface SymbolLookup
SymbolLookup是Java平台的预览API。
仅当启用预览功能时,程序才能使用SymbolLookup
预览功能可能会在将来的版本中被移除,或升级为Java平台的永久功能。
符号查找检索一个或多个库中符号的地址。符号是一个命名实体,例如函数或全局变量。

符号查找是针对特定库(或多个库)创建的。随后,find(String)方法接受符号的名称,并返回该库中符号的地址。

符号的地址被建模为零长度的内存段预览。该段可以以不同方式使用:

  • 它可以传递给Linker预览,以创建一个downcall方法句柄,然后可以使用该句柄调用段地址处的外部函数。
  • 它可以作为参数传递给现有的downcall方法句柄预览,作为底层外部函数的参数。
  • 它可以存储预览在另一个内存段中。
  • 它可以用于访问支持全局变量的内存区域(这需要首先调整大小预览段)。

获取符号查找

工厂方法libraryLookup(String, Arena)libraryLookup(Path, Arena)为操作系统已知的库创建一个符号查找。库可以通过名称或路径指定。如果尚未加载库,则加载库。符号查找,也称为库查找,其生命周期由arena预览控制。例如,如果提供的arena是受限制的arena,则与符号查找关联的库在受限制的arena关闭预览时卸载:
 try (Arena arena = Arena.ofConfined()) {
     SymbolLookup libGL = SymbolLookup.libraryLookup("libGL.so", arena); // libGL.so在此处加载
     MemorySegment glGetString = libGL.find("glGetString").orElseThrow();
     ...
 } //  libGL.so在此处卸载

如果通过JNI之前加载了库,即通过System.load(String)System.loadLibrary(String),则该库也与特定类加载器关联。工厂方法loaderLookup()为调用者的类加载器关联的所有库创建一个符号查找:

System.loadLibrary("GL"); // libGL.so在此处加载
...
SymbolLookup libGL = SymbolLookup.loaderLookup();
MemorySegment glGetString = libGL.find("glGetString").orElseThrow();
此符号查找,也称为加载器查找,与与类加载器关联的库动态关联。如果随后通过JNI加载其他库并将其与类加载器关联,则加载器查找将自动公开其符号。 System.load(String)System.loadLibrary(String)加载的库。加载器查找不公开通过创建库查找加载的库中的符号:

 libraryLookup("libGL.so", arena).find("glGetString").isPresent(); // true
 loaderLookup().find("glGetString").isPresent(); // false
还要注意,对于库 L的库查找公开 L中的符号,即使 L之前通过JNI加载(与类加载器的关联与库查找无关):
 System.loadLibrary("GL"); // libGL.so在此处加载
 libraryLookup("libGL.so", arena).find("glGetString").isPresent(); // true

最后,每个Linker预览为操作系统和处理器组合支持的常用库提供一个符号查找。此符号查找,也称为默认查找,帮助客户快速找到众所周知符号的地址。例如,Linux/x64的Linker预览可能选择通过默认查找公开libc中的符号:

 Linker nativeLinker = Linker.nativeLinker();
 SymbolLookup stdlib = nativeLinker.defaultLookup();
 MemorySegment malloc = stdlib.find("malloc").orElseThrow();
  • Method Details

    • find

      返回具有给定名称的符号的地址。
      参数:
      name - 符号名称。
      返回:
      如果找到,则返回一个指示符号地址的零长度内存段。
    • or

      返回一个组合符号查找,如果找到此查找中的符号,则返回该查找的结果,否则返回另一个查找中的符号的结果。
      API注释:
      此方法可用于链接多个符号查找,例如,以便按顺序从多个库中检索符号:
       var lookup = SymbolLookup.libraryLookup("foo", arena)
               .or(SymbolLookup.libraryLookup("bar", arena))
               .or(SymbolLookup.loaderLookup());
      
      上述代码创建一个符号查找,首先在"foo"库中搜索符号。如果在"foo"中找不到符号,则搜索"bar"。最后,如果在"foo"和"bar"中都找不到符号,则使用加载器查找
      参数:
      other - 应用于查找此查找中未找到的符号的符号查找。
      返回:
      一个组合符号查找,如果找到此查找中的符号,则返回该查找的结果,否则返回另一个查找的结果。
    • loaderLookup

      static SymbolLookupPREVIEW loaderLookup()
      返回与调用者的类加载器关联的库中符号的查找。

      当库通过调用System.load(String)System.loadLibrary(String)从由CL定义的类中的代码加载时,库与类加载器CL关联。如果该代码进一步调用System.load(String)System.loadLibrary(String),则会加载更多库并与CL关联。此方法返回的符号查找始终是当前的:它反映了与相关类加载器关联的所有库,即使它们是在此方法返回后加载的。

      与类加载器关联的库在类加载器变得不可达时被卸载。此方法返回的符号查找与一个新的scopePREVIEW关联,该scope使调用者的类加载器保持可达。因此,与调用者的类加载器关联的库将保持加载(及其符号可用),只要对该类加载器的加载器查找或其获取的任何段之一是可达的。

      在从堆栈上没有调用者帧的上下文中调用此方法的情况下(例如,直接从JNI附加的线程中调用时),调用者的类加载器默认为系统类加载器

      返回:
      与调用者的类加载器关联的库中符号的查找。
      参见:
    • libraryLookup

      static SymbolLookupPREVIEW libraryLookup(String name, ArenaPREVIEW arena)
      加载具有给定名称的库(如果尚未加载)并为该库中的符号创建符号查找。返回的库查找的生命周期由提供的arena控制。例如,如果提供的arena是受限制的arena,则与返回的查找关联的库将在提供的受限制的arena被关闭PREVIEW时卸载。

      此方法是受限制的。受限制的方法是不安全的,如果使用不正确,可能会导致JVM崩溃,或者更糟糕的是,悄无声息地导致内存损坏。因此,客户端应避免依赖受限制的方法,尽可能使用安全和受支持的功能。

      实现注意:
      解析库名称的过程是特定于操作系统的。例如,在符合POSIX的操作系统中,库名称根据该操作系统的dlopen函数的规范解析。在Windows中,库名称根据LoadLibrary函数的规范解析。
      参数:
      name - 库中应查找符号的名称。
      arena - 从返回的查找中获取的符号与之关联的arena。
      返回:
      适合在具有给定名称的库中查找符号的新符号查找。
      抛出:
      IllegalStateException - 如果arena.scope().isAlive() == false
      WrongThreadException - 如果arena是受限制的arena,并且从线程T调用此方法,而不是arena的所有者线程。
      IllegalArgumentException - 如果name未标识有效的库。
      IllegalCallerException - 如果调用者位于未启用本机访问的模块中。
    • libraryLookup

      static SymbolLookupPREVIEW libraryLookup(Path path, ArenaPREVIEW arena)
      从给定路径加载库(如果尚未加载)并为该库中的符号创建符号查找。返回的库查找的生命周期由提供的arena控制。例如,如果提供的arena是受限制的arena,则与返回的查找关联的库将在提供的受限制的arena被关闭PREVIEW时卸载。

      此方法是受限制的。受限制的方法是不安全的,如果使用不正确,可能会导致JVM崩溃,或者更糟糕的是,悄无声息地导致内存损坏。因此,客户端应避免依赖受限制的方法,尽可能使用安全和受支持的功能。

      实现注意:
      在Linux上,此工厂方法提供的功能以及返回的符号查找是使用dlopendlsymdlclose函数实现的。
      参数:
      path - 库中应查找符号的路径。
      arena - 从返回的查找中获取的符号与之关联的arena。
      返回:
      适合在具有给定路径的库中查找符号的新符号查找。
      抛出:
      IllegalStateException - 如果arena.scope().isAlive() == false
      WrongThreadException - 如果arena是受限制的arena,并且从线程T调用此方法,而不是arena的所有者线程。
      IllegalArgumentException - 如果path未指向有效的库。
      IllegalCallerException - 如果调用者位于未启用本机访问的模块中。