当前位置: 首页 > article >正文

openjdk17 从C++视角 看字节码ldc指令字符串加载过程

解析代码

  1. 常量池的解析 (resolve_constant_at_impl)

    • 这个函数主要负责根据常量池中的索引解析常量,并返回一个对象指针 (oop)。对于每种常量类型,代码根据常量池中的标记 (constantTag) 执行不同的解析操作。
    • 主要的常量类型包括:类 (JVM_CONSTANT_Class)、字符串 (JVM_CONSTANT_String)、整数、浮动点数、动态常量 (JVM_CONSTANT_Dynamic),以及方法引用等。
  2. 字符串常量解析 (JVM_CONSTANT_String)

    • resolve_constant_at_impl 中,当遇到字符串常量 (JVM_CONSTANT_String),会调用 string_at_impl 方法。

    • string_at_impl 方法会检查字符串是否已经被 interned(即是否已经在常量池中存在)。如果字符串已经存在(str != NULL),直接返回该字符串。如果字符串未找到,则通过 StringTable::intern 方法将其放入字符串池中,随后更新常量池中的对应位置。

  3. 是否有新增字符串?

    • 新增字符串:如果常量池中没有这个字符串(即该位置的常量为 NULL),则会通过 StringTable::intern 方法将该字符串 intern 到字符串池中。通过 intern,字符串会被添加到全局字符串池中,保证相同内容的字符串只有一份。

    • 已经存在的字符串:如果字符串已经存在(即常量池位置不为 NULL),则直接返回现有的字符串对象,并不会进行新增操作。

具体流程:

  • 当常量池中的某个位置存储的是字符串常量时,resolve_constant_at_impl 会调用 string_at_impl
  • string_at_impl 中:
    • 首先检查该位置是否已经解析过字符串。如果已经解析过(即 resolved_references()->obj_at(obj_index) 不为 NULL),直接返回已存在的字符串对象。
    • 如果尚未解析过:
      • 使用 Symbol(代表字符串的符号)从常量池中获取未解析的字符串。
      • 调用 StringTable::intern 方法将字符串添加到字符串池中。
      • 更新常量池中的该位置,指向新的字符串对象。

因此,只有当常量池中的某个字符串常量未被解析过时,才会通过 StringTable::intern 进行新增,且这种新增操作仅在常量池中没有对应的字符串对象时发生。

结论:

是的,新增字符串常量只会在常量池中没有该字符串时发生。在常量池中的每个位置存储的是一个字符串常量的索引。如果该索引位置已经存在有效的字符串对象(即已经解析并 intern 过),则不会新增。而如果该位置没有对应的字符串对象,则会进行新增,并将其 intern 到字符串池中。

关键代码解释:

// 在 string_at_impl 方法中
oop str = this_cp->resolved_references()->obj_at(obj_index);  // 获取常量池中的字符串对象
assert(str != Universe::the_null_sentinel(), "");
if (str != NULL) return str;  // 如果已存在,直接返回

// 如果常量池中没有该字符串
Symbol* sym = this_cp->unresolved_string_at(which);  // 获取常量池中的字符串符号
str = StringTable::intern(sym, CHECK_(NULL));  // 将字符串符号 intern 到全局字符串池中
this_cp->string_at_put(which, obj_index, str);  // 更新常量池中的字符串对象

assert(java_lang_String::is_instance(str), "must be string");  // 确保结果是字符串
return str;

总结:

  • 字符串常量会被 新增到字符串池中(intern),前提是常量池中尚未包含该字符串。
  • 如果字符串已存在,则不会新增,而是直接返回已有的字符串对象。

 

##C++源码

##
JRT_ENTRY(void, InterpreterRuntime::resolve_ldc(JavaThread* current, Bytecodes::Code bytecode)) {
  assert(bytecode == Bytecodes::_ldc ||
         bytecode == Bytecodes::_ldc_w ||
         bytecode == Bytecodes::_ldc2_w ||
         bytecode == Bytecodes::_fast_aldc ||
         bytecode == Bytecodes::_fast_aldc_w, "wrong bc");
  ResourceMark rm(current);
  const bool is_fast_aldc = (bytecode == Bytecodes::_fast_aldc ||
                             bytecode == Bytecodes::_fast_aldc_w);
  LastFrameAccessor last_frame(current);
  methodHandle m (current, last_frame.method());
  Bytecode_loadconstant ldc(m, last_frame.bci());

  // Double-check the size.  (Condy can have any type.)
  BasicType type = ldc.result_type();
  switch (type2size[type]) {
  case 2: guarantee(bytecode == Bytecodes::_ldc2_w, ""); break;
  case 1: guarantee(bytecode != Bytecodes::_ldc2_w, ""); break;
  default: ShouldNotReachHere();
  }

  // Resolve the constant.  This does not do unboxing.
  // But it does replace Universe::the_null_sentinel by null.
  oop result = ldc.resolve_constant(CHECK);
  assert(result != NULL || is_fast_aldc, "null result only valid for fast_aldc");

#ifdef ASSERT
  {
    // The bytecode wrappers aren't GC-safe so construct a new one
    Bytecode_loadconstant ldc2(m, last_frame.bci());
    int rindex = ldc2.cache_index();
    if (rindex < 0)
      rindex = m->constants()->cp_to_object_index(ldc2.pool_index());
    if (rindex >= 0) {
      oop coop = m->constants()->resolved_references()->obj_at(rindex);
      oop roop = (result == NULL ? Universe::the_null_sentinel() : result);
      assert(roop == coop, "expected result for assembly code");
    }
  }
#endif
  current->set_vm_result(result);
  if (!is_fast_aldc) {
    // Tell the interpreter how to unbox the primitive.
    guarantee(java_lang_boxing_object::is_instance(result, type), "");
    int offset = java_lang_boxing_object::value_offset(type);
    intptr_t flags = ((as_TosState(type) << ConstantPoolCacheEntry::tos_state_shift)
                      | (offset & ConstantPoolCacheEntry::field_index_mask));
    current->set_vm_result_2((Metadata*)flags);
  }
}
JRT_END


##
oop resolve_cached_constant_at(int cache_index, TRAPS) {
    constantPoolHandle h_this(THREAD, this);
    return resolve_constant_at_impl(h_this, _no_index_sentinel, cache_index, NULL, THREAD);
  }
  
// Called to resolve constants in the constant pool and return an oop.
// Some constant pool entries cache their resolved oop. This is also
// called to create oops from constants to use in arguments for invokedynamic
oop ConstantPool::resolve_constant_at_impl(const constantPoolHandle& this_cp,
                                           int index, int cache_index,
                                           bool* status_return, TRAPS) {
  oop result_oop = NULL;
  Handle throw_exception;

  if (cache_index == _possible_index_sentinel) {
    // It is possible that this constant is one which is cached in the objects.
    // We'll do a linear search.  This should be OK because this usage is rare.
    // FIXME: If bootstrap specifiers stress this code, consider putting in
    // a reverse index.  Binary search over a short array should do it.
    assert(index > 0, "valid index");
    cache_index = this_cp->cp_to_object_index(index);
  }
  assert(cache_index == _no_index_sentinel || cache_index >= 0, "");
  assert(index == _no_index_sentinel || index >= 0, "");

  if (cache_index >= 0) {
    result_oop = this_cp->resolved_references()->obj_at(cache_index);
    if (result_oop != NULL) {
      if (result_oop == Universe::the_null_sentinel()) {
        DEBUG_ONLY(int temp_index = (index >= 0 ? index : this_cp->object_to_cp_index(cache_index)));
        assert(this_cp->tag_at(temp_index).is_dynamic_constant(), "only condy uses the null sentinel");
        result_oop = NULL;
      }
      if (status_return != NULL)  (*status_return) = true;
      return result_oop;
      // That was easy...
    }
    index = this_cp->object_to_cp_index(cache_index);
  }

  jvalue prim_value;  // temp used only in a few cases below

  constantTag tag = this_cp->tag_at(index);

  if (status_return != NULL) {
    // don't trigger resolution if the constant might need it
    switch (tag.value()) {
    case JVM_CONSTANT_Class:
    {
      CPKlassSlot kslot = this_cp->klass_slot_at(index);
      int resolved_klass_index = kslot.resolved_klass_index();
      if (this_cp->resolved_klasses()->at(resolved_klass_index) == NULL) {
        (*status_return) = false;
        return NULL;
      }
      // the klass is waiting in the CP; go get it
      break;
    }
    case JVM_CONSTANT_String:
    case JVM_CONSTANT_Integer:
    case JVM_CONSTANT_Float:
    case JVM_CONSTANT_Long:
    case JVM_CONSTANT_Double:
      // these guys trigger OOM at worst
      break;
    default:
      (*status_return) = false;
      return NULL;
    }
    // from now on there is either success or an OOME
    (*status_return) = true;
  }

  switch (tag.value()) {

  case JVM_CONSTANT_UnresolvedClass:
  case JVM_CONSTANT_UnresolvedClassInError:
  case JVM_CONSTANT_Class:
    {
      assert(cache_index == _no_index_sentinel, "should not have been set");
      Klass* resolved = klass_at_impl(this_cp, index, CHECK_NULL);
      // ldc wants the java mirror.
      result_oop = resolved->java_mirror();
      break;
    }

  case JVM_CONSTANT_Dynamic:
    {
      // Resolve the Dynamically-Computed constant to invoke the BSM in order to obtain the resulting oop.
      BootstrapInfo bootstrap_specifier(this_cp, index);

      // The initial step in resolving an unresolved symbolic reference to a
      // dynamically-computed constant is to resolve the symbolic reference to a
      // method handle which will be the bootstrap method for the dynamically-computed
      // constant. If resolution of the java.lang.invoke.MethodHandle for the bootstrap
      // method fails, then a MethodHandleInError is stored at the corresponding
      // bootstrap method's CP index for the CONSTANT_MethodHandle_info. No need to
      // set a DynamicConstantInError here since any subsequent use of this
      // bootstrap method will encounter the resolution of MethodHandleInError.
      // Both the first, (resolution of the BSM and its static arguments), and the second tasks,
      // (invocation of the BSM), of JVMS Section 5.4.3.6 occur within invoke_bootstrap_method()
      // for the bootstrap_specifier created above.
      SystemDictionary::invoke_bootstrap_method(bootstrap_specifier, THREAD);
      Exceptions::wrap_dynamic_exception(/* is_indy */ false, THREAD);
      if (HAS_PENDING_EXCEPTION) {
        // Resolution failure of the dynamically-computed constant, save_and_throw_exception
        // will check for a LinkageError and store a DynamicConstantInError.
        save_and_throw_exception(this_cp, index, tag, CHECK_NULL);
      }
      result_oop = bootstrap_specifier.resolved_value()();
      BasicType type = Signature::basic_type(bootstrap_specifier.signature());
      if (!is_reference_type(type)) {
        // Make sure the primitive value is properly boxed.
        // This is a JDK responsibility.
        const char* fail = NULL;
        if (result_oop == NULL) {
          fail = "null result instead of box";
        } else if (!is_java_primitive(type)) {
          // FIXME: support value types via unboxing
          fail = "can only handle references and primitives";
        } else if (!java_lang_boxing_object::is_instance(result_oop, type)) {
          fail = "primitive is not properly boxed";
        }
        if (fail != NULL) {
          // Since this exception is not a LinkageError, throw exception
          // but do not save a DynamicInError resolution result.
          // See section 5.4.3 of the VM spec.
          THROW_MSG_NULL(vmSymbols::java_lang_InternalError(), fail);
        }
      }

      LogTarget(Debug, methodhandles, condy) lt_condy;
      if (lt_condy.is_enabled()) {
        LogStream ls(lt_condy);
        bootstrap_specifier.print_msg_on(&ls, "resolve_constant_at_impl");
      }
      break;
    }

  case JVM_CONSTANT_String:
    assert(cache_index != _no_index_sentinel, "should have been set");
    result_oop = string_at_impl(this_cp, index, cache_index, CHECK_NULL);
    break;

  case JVM_CONSTANT_DynamicInError:
  case JVM_CONSTANT_MethodHandleInError:
  case JVM_CONSTANT_MethodTypeInError:
    {
      throw_resolution_error(this_cp, index, CHECK_NULL);
      break;
    }

  case JVM_CONSTANT_MethodHandle:
    {
      int ref_kind                 = this_cp->method_handle_ref_kind_at(index);
      int callee_index             = this_cp->method_handle_klass_index_at(index);
      Symbol*  name =      this_cp->method_handle_name_ref_at(index);
      Symbol*  signature = this_cp->method_handle_signature_ref_at(index);
      constantTag m_tag  = this_cp->tag_at(this_cp->method_handle_index_at(index));
      { ResourceMark rm(THREAD);
        log_debug(class, resolve)("resolve JVM_CONSTANT_MethodHandle:%d [%d/%d/%d] %s.%s",
                              ref_kind, index, this_cp->method_handle_index_at(index),
                              callee_index, name->as_C_string(), signature->as_C_string());
      }

      Klass* callee = klass_at_impl(this_cp, callee_index, CHECK_NULL);

      // Check constant pool method consistency
      if ((callee->is_interface() && m_tag.is_method()) ||
          ((!callee->is_interface() && m_tag.is_interface_method()))) {
        ResourceMark rm(THREAD);
        stringStream ss;
        ss.print("Inconsistent constant pool data in classfile for class %s. "
                 "Method '", callee->name()->as_C_string());
        signature->print_as_signature_external_return_type(&ss);
        ss.print(" %s(", name->as_C_string());
        signature->print_as_signature_external_parameters(&ss);
        ss.print(")' at index %d is %s and should be %s",
                 index,
                 callee->is_interface() ? "CONSTANT_MethodRef" : "CONSTANT_InterfaceMethodRef",
                 callee->is_interface() ? "CONSTANT_InterfaceMethodRef" : "CONSTANT_MethodRef");
        THROW_MSG_NULL(vmSymbols::java_lang_IncompatibleClassChangeError(), ss.as_string());
      }

      Klass* klass = this_cp->pool_holder();
      Handle value = SystemDictionary::link_method_handle_constant(klass, ref_kind,
                                                                   callee, name, signature,
                                                                   THREAD);
      result_oop = value();
      if (HAS_PENDING_EXCEPTION) {
        save_and_throw_exception(this_cp, index, tag, CHECK_NULL);
      }
      break;
    }

  case JVM_CONSTANT_MethodType:
    {
      Symbol*  signature = this_cp->method_type_signature_at(index);
      { ResourceMark rm(THREAD);
        log_debug(class, resolve)("resolve JVM_CONSTANT_MethodType [%d/%d] %s",
                              index, this_cp->method_type_index_at(index),
                              signature->as_C_string());
      }
      Klass* klass = this_cp->pool_holder();
      Handle value = SystemDictionary::find_method_handle_type(signature, klass, THREAD);
      result_oop = value();
      if (HAS_PENDING_EXCEPTION) {
        save_and_throw_exception(this_cp, index, tag, CHECK_NULL);
      }
      break;
    }

  case JVM_CONSTANT_Integer:
    assert(cache_index == _no_index_sentinel, "should not have been set");
    prim_value.i = this_cp->int_at(index);
    result_oop = java_lang_boxing_object::create(T_INT, &prim_value, CHECK_NULL);
    break;

  case JVM_CONSTANT_Float:
    assert(cache_index == _no_index_sentinel, "should not have been set");
    prim_value.f = this_cp->float_at(index);
    result_oop = java_lang_boxing_object::create(T_FLOAT, &prim_value, CHECK_NULL);
    break;

  case JVM_CONSTANT_Long:
    assert(cache_index == _no_index_sentinel, "should not have been set");
    prim_value.j = this_cp->long_at(index);
    result_oop = java_lang_boxing_object::create(T_LONG, &prim_value, CHECK_NULL);
    break;

  case JVM_CONSTANT_Double:
    assert(cache_index == _no_index_sentinel, "should not have been set");
    prim_value.d = this_cp->double_at(index);
    result_oop = java_lang_boxing_object::create(T_DOUBLE, &prim_value, CHECK_NULL);
    break;

  default:
    DEBUG_ONLY( tty->print_cr("*** %p: tag at CP[%d/%d] = %d",
                              this_cp(), index, cache_index, tag.value()));
    assert(false, "unexpected constant tag");
    break;
  }

  if (cache_index >= 0) {
    // Benign race condition:  resolved_references may already be filled in.
    // The important thing here is that all threads pick up the same result.
    // It doesn't matter which racing thread wins, as long as only one
    // result is used by all threads, and all future queries.
    oop new_result = (result_oop == NULL ? Universe::the_null_sentinel() : result_oop);
    oop old_result = this_cp->resolved_references()
      ->atomic_compare_exchange_oop(cache_index, new_result, NULL);
    if (old_result == NULL) {
      return result_oop;  // was installed
    } else {
      // Return the winning thread's result.  This can be different than
      // the result here for MethodHandles.
      if (old_result == Universe::the_null_sentinel())
        old_result = NULL;
      return old_result;
    }
  } else {
    assert(result_oop != Universe::the_null_sentinel(), "");
    return result_oop;
  }
}

##
oop ConstantPool::string_at_impl(const constantPoolHandle& this_cp, int which, int obj_index, TRAPS) {
  // If the string has already been interned, this entry will be non-null
  oop str = this_cp->resolved_references()->obj_at(obj_index);
  assert(str != Universe::the_null_sentinel(), "");
  if (str != NULL) return str;
  Symbol* sym = this_cp->unresolved_string_at(which);
  str = StringTable::intern(sym, CHECK_(NULL));
  this_cp->string_at_put(which, obj_index, str);
  assert(java_lang_String::is_instance(str), "must be string");
  return str;
}  

##gdb调试堆栈

#0  typeArrayOopDesc::object_size (lh=-1072691200, length=3) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/oops/typeArrayOop.hpp:120
#1  0x00007ffff6b8bf1d in TypeArrayKlass::allocate_common (this=0x100040820, length=3, do_zero=true, __the_thread__=0x7ffff0028920)
    at /home/yym/openjdk17/jdk17-master/src/hotspot/share/oops/typeArrayKlass.cpp:92
#2  0x00007ffff60b06c8 in TypeArrayKlass::allocate (this=0x100040820, length=3, __the_thread__=0x7ffff0028920)
    at /home/yym/openjdk17/jdk17-master/src/hotspot/share/oops/typeArrayKlass.hpp:68
#3  0x00007ffff685be50 in oopFactory::new_byteArray (length=3, __the_thread__=0x7ffff0028920) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/memory/oopFactory.cpp:60
#4  0x00007ffff6379b92 in java_lang_String::basic_create (length=3, is_latin1=true, __the_thread__=0x7ffff0028920)
    at /home/yym/openjdk17/jdk17-master/src/hotspot/share/classfile/javaClasses.cpp:285
#5  0x00007ffff6379c77 in java_lang_String::create_from_unicode (unicode=0x7fff7007d0e0, length=3, __the_thread__=0x7ffff0028920)
    at /home/yym/openjdk17/jdk17-master/src/hotspot/share/classfile/javaClasses.cpp:297
#6  0x00007ffff6a8da0d in StringTable::do_intern (string_or_null_h=..., name=0x7fff7007d0e0, len=3, hash=113046, __the_thread__=0x7ffff0028920)
    at /home/yym/openjdk17/jdk17-master/src/hotspot/share/classfile/stringTable.cpp:349
#7  0x00007ffff6a8d96e in StringTable::intern (string_or_null_h=..., name=0x7fff7007d0e0, len=3, __the_thread__=0x7ffff0028920)
    at /home/yym/openjdk17/jdk17-master/src/hotspot/share/classfile/stringTable.cpp:338
#8  0x00007ffff6a8d6a0 in StringTable::intern (symbol=0x7ffff0529fd0, __the_thread__=0x7ffff0028920)
    at /home/yym/openjdk17/jdk17-master/src/hotspot/share/classfile/stringTable.cpp:298
#9  0x00007ffff606fca4 in ConstantPool::string_at_impl (this_cp=..., which=77, obj_index=12, __the_thread__=0x7ffff0028920)
    at /home/yym/openjdk17/jdk17-master/src/hotspot/share/oops/constantPool.cpp:1227
#10 0x00007ffff606ed90 in ConstantPool::resolve_constant_at_impl (this_cp=..., index=77, cache_index=12, status_return=0x0, __the_thread__=0x7ffff0028920)
    at /home/yym/openjdk17/jdk17-master/src/hotspot/share/oops/constantPool.cpp:1051
#11 0x00007ffff5df5f7d in ConstantPool::resolve_cached_constant_at (this=0x7fffd9c0f108, cache_index=12, __the_thread__=0x7ffff0028920)
    at /home/yym/openjdk17/jdk17-master/src/hotspot/share/oops/constantPool.hpp:743
#12 0x00007ffff5df58a8 in Bytecode_loadconstant::resolve_constant (this=0x7ffff7bfe6a0, __the_thread__=0x7ffff0028920)
    at /home/yym/openjdk17/jdk17-master/src/hotspot/share/interpreter/bytecode.cpp:215
#13 0x00007ffff6368055 in InterpreterRuntime::resolve_ldc (current=0x7ffff0028920, bytecode=Bytecodes::_fast_aldc)
    at /home/yym/openjdk17/jdk17-master/src/hotspot/share/interpreter/interpreterRuntime.cpp:186
#14 0x00007fffe10279b7 in ?? ()
#15 0x00007fffe102792b in ?? ()
#16 0x00000000ffa1a0d0 in ?? ()
#17 0x00007ffff7bfe7a0 in ?? ()
#18 0x00007fffd9c0fae3 in ?? ()
#19 0x00007ffff7bfe808 in ?? ()
#20 0x00007fffd9c0e800 in ?? ()
#21 0x0000000000000000 in ?? ()


http://www.kler.cn/a/448740.html

相关文章:

  • Android 常用布局
  • Redis分布式锁释放锁是否必须用lua脚本?
  • 深度学习实战车辆目标跟踪【bytetrack/deepsort】
  • 如何在window 使用 conda 环境下载大模型
  • 36. Three.js案例-创建带光照和阴影的球体与平面
  • 「配置应用的可见性」功能使用教程
  • 三、使用langchain搭建RAG:金融问答机器人--检索增强生成
  • docker 为单个容器设置代理
  • Input子系统驱动---学习记录
  • 整理一些/etc/X11/xorg.conf,/etc/X11/xorg.conf.d相关知识的学习笔记
  • 微服务openfeign配置重试机制
  • Unittest01|TestCase、断言、装饰器、夹具、清理函数、ddt
  • FastAPI vs Go 性能对比分析
  • 语言模型与向量模型:深入解析与实例剖析
  • PHP中实现拓扑算法
  • Bazel CI
  • 基于 SSM 和 Vue 的 WEB 开放性实验室集成管理系统
  • 【leetcode100】排序链表
  • springboot根据租户id动态指定数据源
  • react Moment.js 是一个流行的 JavaScript 库,用于处理日期和时间。它提供了丰富的功能,包括日期格式化、解析、操作和国际化
  • 前端 下载文件时如何处理后端返回的 文件流
  • Vulkan 学习(10)---- Vulkan SwapChain 创建
  • 实现 React 电子签名功能:从零开始构建一个完整的解决方案
  • Unity全局雾效
  • 深度学习革新音乐转录
  • MQTT实现集群分布式消费