在 Java 中,/(即正斜杠)在 $Lambda$15/0x00000008000a9440@32e6e9c3 等对象引用中意味着什么?

2024-02-28

在 JShell 中,如果我这样做:

interface Foo { String foo(); }
(Foo) () -> "hi"

I get

|  created interface Foo
$2 ==> $Lambda$15/0x00000008000a9440@32e6e9c3

通过以下研究,我知道以下内容:

$Lambda =对生成的字节码的内存中引用,而不是由匿名内部类 (AIC) 持久保存到磁盘的引用 https://blogs.oracle.com/javamagazine/post/behind-the-scenes-how-do-lambda-expressions-really-work-in-java

$15 = 对 AIC 的对象引用 https://stackoverflow.com/questions/11388840/java-compiled-classes-contain-dollar-signs/11388863#11388863

@32e6e9c3 =创建对象的序列号——至少在 IntelliJ 中 https://stackoverflow.com/questions/50624284/while-debugging-java-codes-what-does-mean-in-statements-like-instance789-or/50625530#50625530

但什么是/(斜杠)表示,如/0x00000008000a9440?


Summary

$Lambda$15/0x00000008000a9440是创建的隐藏类的名称。

如下所示,0x00000008000a9440称为后缀。

可以通过调用来检索类的名称java.lang.Class.getName()方法。 所以:

  • 例如,相同的类名可以通过 Java 程序(而不是通过 JShell)检索。
  • 问题似乎不是关于JShell,而是关于Java语言和Java虚拟机。

显示隐藏类名称的示例程序

Program class

package info.brunov.stackoverflow.question72804142;

import java.util.function.Supplier;

public final class Program {
    public static void main(final String args[]) {
        printRuntimeInformation();

        final Supplier<String> supplier1 = () -> "";
        final Supplier<String> supplier2 = () -> "";
        final Supplier<String> supplier3 = () -> "";
        System.out.println(
            String.format("Supplier 1: %s", supplier1.getClass().getName())
        );
        System.out.println(
            String.format("Supplier 2: %s", supplier2.getClass().getName())
        );
        System.out.println(
            String.format("Supplier 3: %s", supplier3.getClass().getName())
        );
    }

    private static void printRuntimeInformation() {
        System.out.println(
            String.format(
                "Java Virtual Machine specification name: %s",
                System.getProperty("java.vm.specification.name")
            )
        );
        System.out.println(
            String.format(
                "Java Virtual Machine specification version: %s",
                System.getProperty("java.vm.specification.version")
            )
        );
        System.out.println(
            String.format(
                "Java Virtual Machine specification vendor: %s",
                System.getProperty("java.vm.specification.vendor")
            )
        );
        System.out.println(
            String.format(
                "Java Virtual Machine implementation name: %s",
                System.getProperty("java.vm.name")
            )
        );
        System.out.println(
            String.format(
                "Java Virtual Machine implementation version: %s",
                System.getProperty("java.vm.version")
            )
        );
        System.out.println(
            String.format(
                "Java Virtual Machine implementation vendor: %s",
                System.getProperty("java.vm.vendor")
            )
        );
    }
}

程序输出

Java Virtual Machine specification name: Java Virtual Machine Specification
Java Virtual Machine specification version: 18
Java Virtual Machine specification vendor: Oracle Corporation
Java Virtual Machine implementation name: OpenJDK 64-Bit Server VM
Java Virtual Machine implementation version: 18.0.1-ea+10-Debian-1
Java Virtual Machine implementation vendor: Debian
Supplier 1: info.brunov.stackoverflow.question72804142.Program$$Lambda$18/0x0000000800c031f0
Supplier 2: info.brunov.stackoverflow.question72804142.Program$$Lambda$19/0x0000000800c033f8
Supplier 3: info.brunov.stackoverflow.question72804142.Program$$Lambda$20/0x0000000800c03600

文档参考

JEP 371:隐藏类

隐藏类是从 JDK 15 开始引入的。 有关更多详细信息,请参阅 JEP:JEP 371:隐藏类 https://openjdk.org/jeps/371.

以下是 JEP 中有关隐藏类名称的摘录:

创建隐藏类的主要区别在于它所指定的名称。隐藏类不是匿名的。它有一个可通过以下方式获得的名称Class::getName并且可能会显示在诊断中(例如输出java -verbose:class)、JVM TI 类加载事件、JFR 事件和堆栈跟踪中。然而,该名称的形式非常不寻常,它实际上使该类对所有其他类不可见。该名称是以下内容的串联:

  1. 内部形式 (JVMS 4.2.1) 指定的二进制名称this_class in the ClassFile结构,说A/B/C;
  2. The '.'特点;和
  3. 由 JVM 实现选择的非限定名称 (JVMS 4.2.2)。

例如,如果this_class指定com/example/Foo(二进制名称的内部形式com.example.Foo),然后派生出一个隐藏类ClassFile结构可以命名为com/example/Foo.1234。该字符串既不是二进制名称,也不是二进制名称的内部形式。

给定一个隐藏类,其名称为A/B/C.x, 的结果Class::getName是以下的串联:

  1. 二进制名称A.B.C(通过采取A/B/C并替换每个'/' with '.');
  2. 人物;和
  3. 不合格名称x.

例如,如果隐藏类被命名为com/example/Foo.1234,那么结果是Class::getName is com.example.Foo/1234。同样,该字符串既不是二进制名称,也不是二进制名称的内部形式。

隐藏类的命名空间与普通类的命名空间不相交。给定一个ClassFile结构其中this_class指定com/example/Foo/1234,调用cl.defineClass("com.example.Foo.1234", bytes, ...)仅仅产生一个名为的普通类com.example.Foo.1234,与名为的隐藏类不同com.example.Foo/1234。不可能创建一个名为的普通类com.example.Foo/1234因为cl.defineClass("com.example.Foo/1234", bytes, ...)将拒绝字符串参数,因为它不是二进制名称。

Java文档:java.lang.Class#getName() method

我们参考方法文档:类(Java SE 15 和 JDK 15) https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/Class.html#getName().

文档摘录:

public String https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/String.html获取名称()

返回由此表示的实体(类、接口、数组类、基本类型或 void)的名称Class object.

If this Classobject 表示类或接口,而不是数组类,那么:

  • 如果类或接口不是hidden https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/Class.html#isHidden(),那么二进制名称 https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/ClassLoader.html#binary-name返回类或接口的类型。
  • 如果类或接口被隐藏,则结果是以下形式的字符串:N + '/' + <suffix> where N is the 二进制名称 https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/ClassLoader.html#binary-name表示由class文件传递到Lookup::defineHiddenClass https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/invoke/MethodHandles.Lookup.html#defineHiddenClass(byte%5B%5D,boolean,java.lang.invoke.MethodHandles.Lookup.ClassOption...), and <suffix>是一个不合格的名称。

实现细节:OpenJDK Java 虚拟机:隐藏类名

介绍

让我们考虑一下 OpenJDK 18 的源代码。

我们参考一下标签:openjdk/jdk18 at jdk-18+37 https://github.com/openjdk/jdk18/tree/jdk-18%2B37.

请注意:

  • 以下执行路径是理论上的:我正在使用提到的源代码标签。
  • 下面的调用堆栈是真实的:我正在使用 OpenJDK18.0.1-ea+10-Debian-1.

隐藏类名修改

隐藏类的创建(java.lang.invoke.MethodHandles.Lookup.defineHiddenClass()方法)包括对其名称的修改。

让我们考虑以下调用堆栈:

"main@1" prio=5 tid=0x1 nid=NA runnable
  java.lang.Thread.State: RUNNABLE
      at java.lang.System$2.defineClass(System.java:2346)
      at java.lang.invoke.MethodHandles$Lookup$ClassDefiner.defineClass(MethodHandles.java:2432)
      at java.lang.invoke.MethodHandles$Lookup$ClassDefiner.defineClassAsLookup(MethodHandles.java:2413)
      at java.lang.invoke.MethodHandles$Lookup.defineHiddenClass(MethodHandles.java:2119)
      at java.lang.invoke.InnerClassLambdaMetafactory.generateInnerClass(InnerClassLambdaMetafactory.java:385)
      at java.lang.invoke.InnerClassLambdaMetafactory.spinInnerClass(InnerClassLambdaMetafactory.java:293)
      at java.lang.invoke.InnerClassLambdaMetafactory.buildCallSite(InnerClassLambdaMetafactory.java:228)
      at java.lang.invoke.LambdaMetafactory.metafactory(LambdaMetafactory.java:341)
      at java.lang.invoke.DirectMethodHandle$Holder.invokeStatic(DirectMethodHandle$Holder:-1)
      at java.lang.invoke.Invokers$Holder.invokeExact_MT(Invokers$Holder:-1)
      at java.lang.invoke.BootstrapMethodInvoker.invoke(BootstrapMethodInvoker.java:134)
      at java.lang.invoke.CallSite.makeSite(CallSite.java:315)
      at java.lang.invoke.MethodHandleNatives.linkCallSiteImpl(MethodHandleNatives.java:279)
      at java.lang.invoke.MethodHandleNatives.linkCallSite(MethodHandleNatives.java:269)
      at info.brunov.stackoverflow.question72804142.Program.main(Program.java:9)

然后我们考虑以下执行路径调用堆栈的延续:

Class<?> java.lang.ClassLoader#defineClass0(ClassLoader loader, Class<?> lookup, String name, byte[] b, int off, int len, ProtectionDomain pd, boolean initialize, int flags, Object classData)

// Native calls below.
jclass Unsafe_DefineClass0(JNIEnv *env, jobject unsafe, jstring name, jbyteArray data, int offset, int length, jobject loader, jobject pd)
jclass Unsafe_DefineClass_impl(JNIEnv *env, jstring name, jbyteArray data, int offset, int length, jobject loader, jobject pd)
JNIEXPORT jclass JNICALL
jclass JVM_DefineClass(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jsize len, jobject pd)
jclass jvm_define_class_common(const char *name, jobject loader, const jbyte *buf, jsize len, jobject pd, const char *source, TRAPS)
InstanceKlass* SystemDictionary::resolve_from_stream(ClassFileStream* st, Symbol* class_name, Handle class_loader, const ClassLoadInfo& cl_info, TRAPS)
InstanceKlass* SystemDictionary::resolve_hidden_class_from_stream(ClassFileStream* st, Symbol* class_name, Handle class_loader, const ClassLoadInfo& cl_info, TRAPS)
InstanceKlass* KlassFactory::create_from_stream(ClassFileStream* stream, Symbol* name, ClassLoaderData* loader_data, const ClassLoadInfo& cl_info, TRAPS)
InstanceKlass* ClassFileParser::create_instance_klass(bool changed_by_loadhook, const ClassInstanceInfo& cl_inst_info, TRAPS)
void ClassFileParser::mangle_hidden_class_name(InstanceKlass* const ik)

我们参考一下源码:jdk18/classFileParser.cpp 位于 jdk-18+37 · openjdk/jdk18 https://github.com/openjdk/jdk18/blob/jdk-18+37/src/hotspot/share/classfile/classFileParser.cpp#L5920-L5938:

void ClassFileParser::mangle_hidden_class_name(InstanceKlass* const ik) {
  ResourceMark rm;
  // Construct hidden name from _class_name, "+", and &ik. Note that we can't
  // use a '/' because that confuses finding the class's package.  Also, can't
  // use an illegal char such as ';' because that causes serialization issues
  // and issues with hidden classes that create their own hidden classes.
  char addr_buf[20];
  if (DumpSharedSpaces) {
    // We want stable names for the archived hidden classes (only for static
    // archive for now). Spaces under default_SharedBaseAddress() will be
    // occupied by the archive at run time, so we know that no dynamically
    // loaded InstanceKlass will be placed under there.
    static volatile size_t counter = 0;
    Atomic::cmpxchg(&counter, (size_t)0, Arguments::default_SharedBaseAddress()); // initialize it
    size_t new_id = Atomic::add(&counter, (size_t)1);
    jio_snprintf(addr_buf, 20, SIZE_FORMAT_HEX, new_id);
  } else {
    jio_snprintf(addr_buf, 20, INTPTR_FORMAT, p2i(ik));
  }

请注意,+字符用作分隔符。

获取隐藏类名

The java.lang.Class#getName()方法包括字符替换:+被替换为/.

让我们考虑以下执行路径:

String java.lang.Class.getName()
String java.lang.Class.initClassName()

// Native calls below.
JNIEXPORT jstring JNICALL JVM_InitClassName(JNIEnv *env, jclass cls)
oop java_lang_Class::name(Handle java_class, TRAPS)
const char* java_lang_Class::as_external_name(oop java_class)
const char* Klass::external_name() const
static char* convert_hidden_name_to_java(Symbol* name)

我们参考一下源码:jdk18/klass.cpp 位于 jdk-18+37 · openjdk/jdk18 https://github.com/openjdk/jdk18/blob/jdk-18%2B37/src/hotspot/share/oops/klass.cpp#L665-L666:

// Replace the last '+' char with '/'.
static char* convert_hidden_name_to_java(Symbol* name) {
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Java 中,/(即正斜杠)在 $Lambda$15/0x00000008000a9440@32e6e9c3 等对象引用中意味着什么? 的相关文章

  • 我可以确定谁在调用 Java 中的函数或实例化类吗? [复制]

    这个问题在这里已经有答案了 可能的重复 在Java中 如何使用堆栈跟踪或反射找到方法的调用者 https stackoverflow com questions 421280 in java how do i find the caller
  • Java中RandomAccessFile的并发

    我正在创建一个RandomAccessFile对象通过多个线程写入文件 在 SSD 上 每个线程都尝试在文件中的特定位置写入直接字节缓冲区 并且我确保线程写入的位置不会与另一个线程重叠 file getChannel write buffe
  • H.323,如何制作一个没有媒体的简单环。该脚本遵循 Q.931 设置,但仍然无法正常工作

    谁能帮我解决这个问题吗 当我发送此请求时 我在wireshark中看到数据包将发送到1720 tcp端口中的SJPhone 但 SJPhone 仍然没有响铃 我想让它响起 无论媒体 我非常感谢您的支持 我一定缺少消息协议细节来实现这个 请给
  • 通过 html tidy 提供渲染 jsp 页面

    我有一个在 Glassfish 上运行的 Java 项目 它会呈现一些难看的 HTML 这是使用各种内部和外部 JSP 库的副作用 我想设置某种渲染后过滤器 通过 HTMLTidy 提供最终的 HTML 这样源代码就很好且整洁 有助于调试
  • Active MQ - HelloWorld 示例异常

    我正在尝试运行 hello world 示例在这里找到 http activemq apache org hello world html I added activemq all 5 5 1 jar已经到图书馆了 它构建成功 但出现以下警
  • Java Spark DataFrameReader java.lang.NegativeArraySizeException

    学习 Spark for java 并尝试阅读 csv文件为DataFrame使用DataFrameReader 甚至不能得到一个超级简单的 csv文件工作 因为我不断收到异常java lang NegativeArraySizeExcep
  • 全静态方法和应用单例模式有什么区别?

    我正在创建一个数据库来存储有关我的网站用户的信息 我正在使用 stuts2 因此使用 Java EE 技术 对于数据库 我将创建一个 DBManager 我应该在这里应用单例模式还是将其所有方法设为静态 我将使用这个 DBManager 进
  • AffineTransform.rotate() - 如何同时缩放、旋转和缩放?

    我有以下代码 它可以完成我想要绘制一个上面有一些棋子的棋盘的 第一部分 Image pieceImage getImage currentPiece int pieceHeight pieceImage getHeight null dou
  • Android 游戏偶尔出现延迟

    我正在用 Java 制作一个简单的 Android 游戏 我注意到每 20 40 秒就会出现一些烦人的延迟 首先 我认为它们是由垃圾收集器引起的 但当我检查 LogCat 时 我发现游戏滞后时没有垃圾收集 每当游戏开始滞后时 我都会标记日志
  • org.apache.commons.codec.digest.Md5Crypt.md5Crypt 函数。 linux下出现异常,windows下正常

    我们正在使用commons codec加密密码 使用org apache commons codec digest Md5Crypt md5Crypt功能 在Windows环境下工作正常 但在CentOS上却抛出异常 我们有3台centOS
  • 为什么我要使用责任链而不是 switch 语句

    考虑一下您已经获得了多次验证 仅当要检查的对象属于某种类型时 这些验证才应生效 为什么我要使用责任链而不是 switch 语句 责任链示例 public class Executor Inject private ValidatorFact
  • java中日期转换dd-MMM-yyyy到dd-MM-yyyy

    在Java中将23 Mar 2011转换为23 03 2011的最简单方法是什么 感谢大家 这似乎解决了这个问题 try Calendar cal Calendar getInstance cal setTime new SimpleDat
  • Java 不可变对象 [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我正在学习不变性的概念 据我了解 一旦创建对象 不可变对象就无法更改其值 但我不明白不可变对象的以下用途 They are 自动是线程
  • bufferedinputstream 中标记读取限制有什么用

    我是Java流的新手 我想读取特定的文件内容 然后需要从头开始读取 我创建了一个 BufferedInputStream 但我对 BufferedInputStream mark int markLimit 的文档感到困惑 文档说 publ
  • 获取包中声明的所有 Java 类的名称

    我正在编写一个功能 它将有助于将类放入我的程序的某个包中 另外 我只想要子类某个类的类 我需要这些类才能调用它们的静态方法 有没有一种自动的方法来做到这一点 如果是的话 速度慢吗 如果我不清楚 我想要的是这样的 ArrayList
  • Java LRU 缓存使用 LinkedList

    堆栈溢出的新手 所以请不要介意我以菜鸟的方式问这个问题 我正在尝试使用链表实现 LRU 缓存 我在这里看到了使用 linkedHashMap 和其他数据结构的其他实现 但对于这种情况 我正在尝试使用链表创建最佳优化版本 正如我在技术期间被问
  • 更新分页。是否可以?

    他们是否存在一些方法来处理更新分页 例如我有 100 行类型 Id private Integer id Column private boolean flag Column private Date last 一开始它们看起来像 id f
  • 如何在速度模板中检索哈希图值

    如何从速度模板中的以下哈希图中检索值 请帮忙 LinkedHashMap
  • 在没有EOF的情况下停止读取java中的输入

    In 问题 如何停止读取输入 我的程序继续运行 要求更多输入 public static void main String args throws Exception BufferedReader br new BufferedReader
  • 获取Java中ResultSet返回的行数

    我用过一个ResultSet返回一定数量的行 我的代码是这样的 ResultSet res getData if res next System out println No Data Found while res next code t

随机推荐