10、jvm源码解读 - ldc指令的解读

写一个java文件

    public static void main(String[] args) {
        String str1="abc";
        String str2 ="abc";
        String str3=new String("abc");
        boolean b1= str1==str2;
        boolean b2= str1==str3;

    }

查看字节码code

 0 ldc #4 <abc>
 2 astore_1
 3 ldc #4 <abc>
 5 astore_2
 6 new #5 <java/lang/String>
 9 dup
10 ldc #4 <abc>
12 invokespecial #6 <java/lang/String.<init>>
15 astore_3
16 aload_1
17 aload_2
18 if_acmpne 25 (+7)
21 iconst_1
22 goto 26 (+4)
25 iconst_0
26 istore 4
28 aload_1
29 aload_3
30 if_acmpne 37 (+7)
33 iconst_1
34 goto 38 (+4)
37 iconst_0
38 istore 5
40 getstatic #7 <java/lang/System.out>
43 ldc #8 <helloworld!>

能看待这个一个是ldc #4,其中#4的类型是

JVM_CONSTANT_String

然后对于String类的解析,会比较明白,先加载java/lang/String类,在生成oop对象,而ldc #4查看字节码的解析如下:

void TemplateTable::ldc(bool wide) {
  transition(vtos, vtos);
  Label call_ldc, notFloat, notClass, Done;

  if (wide) {
    __ get_unsigned_2_byte_index_at_bcp(rbx, 1);
  } else {
    __ load_unsigned_byte(rbx, at_bcp(1));
  }
  __ get_cpool_and_tags(rcx, rax);
  const int base_offset = ConstantPool::header_size() * wordSize;
  const int tags_offset = Array<u1>::base_offset_in_bytes();

  // get type
  __ xorptr(rdx, rdx);
  __ movb(rdx, Address(rax, rbx, Address::times_1, tags_offset));

  // unresolved class - get the resolved class
  __ cmpl(rdx, JVM_CONSTANT_UnresolvedClass);
  __ jccb(Assembler::equal, call_ldc);

  // unresolved class in error (resolution failed) - call into runtime
  // so that the same error from first resolution attempt is thrown.
  __ cmpl(rdx, JVM_CONSTANT_UnresolvedClassInError);
  __ jccb(Assembler::equal, call_ldc);

  // resolved class - need to call vm to get java mirror of the class
  __ cmpl(rdx, JVM_CONSTANT_Class);
  __ jcc(Assembler::notEqual, notClass);

  __ bind(call_ldc);
  __ movl(rcx, wide);
  call_VM(rax, CAST_FROM_FN_PTR(address, InterpreterRuntime::ldc), rcx);
  __ push(atos);
  __ jmp(Done);

  __ bind(notClass);
  __ cmpl(rdx, JVM_CONSTANT_Float);
  __ jccb(Assembler::notEqual, notFloat);
  // ftos
  __ fld_s(    Address(rcx, rbx, Address::times_ptr, base_offset));
  __ push(ftos);
  __ jmp(Done);

  __ bind(notFloat);
#ifdef ASSERT
  { Label L;
    __ cmpl(rdx, JVM_CONSTANT_Integer);
    __ jcc(Assembler::equal, L);
    // String and Object are rewritten to fast_aldc
    __ stop("unexpected tag type in ldc");
    __ bind(L);
  }
#endif
  // itos JVM_CONSTANT_Integer only
  __ movl(rax, Address(rcx, rbx, Address::times_ptr, base_offset));
  __ push(itos);
  __ bind(Done);
}

就算加上了汇编完成的东西

----------------------------------------------------------------------
ldc  18 ldc  [0x00000000033c7840, 0x00000000033c79a0]  352 bytes

0x00000000033c7840: push   %rax
0x00000000033c7841: jmpq   0x00000000033c7870
0x00000000033c7846: sub    $0x8,%rsp
0x00000000033c784a: vmovss %xmm0,(%rsp)
0x00000000033c784f: jmpq   0x00000000033c7870
0x00000000033c7854: sub    $0x10,%rsp
0x00000000033c7858: vmovsd %xmm0,(%rsp)
0x00000000033c785d: jmpq   0x00000000033c7870
0x00000000033c7862: sub    $0x10,%rsp
0x00000000033c7866: mov    %rax,(%rsp)
0x00000000033c786a: jmpq   0x00000000033c7870
0x00000000033c786f: push   %rax
0x00000000033c7870: movzbl 0x1(%r13),%ebx
0x00000000033c7875: mov    -0x18(%rbp),%rcx
0x00000000033c7879: mov    0x8(%rcx),%rcx
0x00000000033c787d: mov    0x8(%rcx),%rcx
0x00000000033c7881: mov    0x8(%rcx),%rax
0x00000000033c7885: movzbl 0x4(%rax,%rbx,1),%edx
// unresolved class - get the resolved class
__ cmpl(rdx, JVM_CONSTANT_UnresolvedClass);
__ jccb(Assembler::equal, call_ldc);
0x00000000033c788a: cmp    $0x64,%edx
0x00000000033c788d: je     0x00000000033c789d
// unresolved class in error state - call into runtime to throw the error
// from the first resolution attempt
__ cmpl(rdx, JVM_CONSTANT_UnresolvedClassInError);
__ jccb(Assembler::equal, call_ldc);
0x00000000033c788f: cmp    $0x67,%edx
0x00000000033c7892: je     0x00000000033c789d
// resolved class - need to call vm to get java mirror of the class
__ cmpl(rdx, JVM_CONSTANT_Class);
__ jcc(Assembler::notEqual, notClass);
0x00000000033c7894: cmp    $0x7,%edx
0x00000000033c7897: jne    0x00000000033c794d
__ movl(c_rarg1, wide); wide=false
0x00000000033c789d: mov    $0x0,%edx
0x00000000033c78a2: callq  0x00000000033c78ac
0x00000000033c78a7: jmpq   0x00000000033c7947
0x00000000033c78ac: lea    0x8(%rsp),%rax
0x00000000033c78b1: mov    %r13,-0x38(%rbp)
0x00000000033c78b5: mov    %r15,%rcx
0x00000000033c78b8: mov    %rbp,0x1e8(%r15)
0x00000000033c78bf: mov    %rax,0x1d8(%r15)
0x00000000033c78c6: sub    $0x20,%rsp
0x00000000033c78ca: test   $0xf,%esp
0x00000000033c78d0: je     0x00000000033c78e8
0x00000000033c78d6: sub    $0x8,%rsp
0x00000000033c78da: callq  0x000000006aad9cd0
0x00000000033c78df: add    $0x8,%rsp
0x00000000033c78e3: jmpq   0x00000000033c78ed
0x00000000033c78e8: callq  0x000000006aad9cd0
0x00000000033c78ed: add    $0x20,%rsp
0x00000000033c78f1: movabs $0x0,%r10
0x00000000033c78fb: mov    %r10,0x1d8(%r15)
0x00000000033c7902: movabs $0x0,%r10
0x00000000033c790c: mov    %r10,0x1e8(%r15)
0x00000000033c7913: cmpq   $0x0,0x8(%r15)
0x00000000033c791b: je     0x00000000033c7926
0x00000000033c7921: jmpq   0x00000000033b07e0
0x00000000033c7926: mov    0x238(%r15),%rax
0x00000000033c792d: movabs $0x0,%r10
0x00000000033c7937: mov    %r10,0x238(%r15)
0x00000000033c793e: mov    -0x38(%rbp),%r13
0x00000000033c7942: mov    -0x30(%rbp),%r14
0x00000000033c7946: retq
0x00000000033c7947: push   %rax
0x00000000033c7948: jmpq   0x00000000033c796b
__ bind(notClass);
__ cmpl(rdx, JVM_CONSTANT_Float); 常量值为4
        __ jccb(Assembler::notEqual, notFloat);
0x00000000033c794d: cmp    $0x4,%edx
0x00000000033c7950: jne    0x00000000033c7966
0x00000000033c7952: vmovss 0x50(%rcx,%rbx,8),%xmm0
0x00000000033c7958: sub    $0x8,%rsp
0x00000000033c795c: vmovss %xmm0,(%rsp)
0x00000000033c7961: jmpq   0x00000000033c796b

__ bind(notFloat);

0x00000000033c7966: mov    0x50(%rcx,%rbx,8),%eax
0x00000000033c796a: push   %rax

//下一条指令取指
0x00000000033c796b: movzbl 0x2(%r13),%ebx
0x00000000033c7970: add    $0x2,%r13
0x00000000033c7974: movabs $0x6b2143f0,%r10
0x00000000033c797e: jmpq   *(%r10,%rbx,8)
0x00000000033c7982: nopw   0x0(%rax,%rax,1)
0x00000000033c7988: add    %al,(%rax)
0x00000000033c798a: add    %al,(%rax)
0x00000000033c798c: add    %al,(%rax)
0x00000000033c798e: add    %al,(%rax)
0x00000000033c7990: add    %al,(%rax)
0x00000000033c7992: add    %al,(%rax)
0x00000000033c7994: add    %al,(%rax)
0x00000000033c7996: add    %al,(%rax)
0x00000000033c7998: add    %al,(%rax)
0x00000000033c799a: add    %al,(%rax)
0x00000000033c799c: add    %al,(%rax)
0x00000000033c799e: add    %al,(%rax)
----------------------------------------------------------------------

也分析不出来,打断点始终打不到,对于ldc 加载类,却可以打到断点,真是迷惑,查了一圈看了一篇文章说会调用StringTable::intern

文章链接:iizhihu.com/question/60778124

但是也没说明白调用链,开头就直接说了

给出以下逻辑

&nbsp;

查看源码也是在bytecodeInterpreter.cpp找到了这段内容

      CASE(_ldc_w):
      CASE(_ldc):
        {
          u2 index;
          bool wide = false;
          int incr = 2; // frequent case
          if (opcode == Bytecodes::_ldc) {
            index = pc[1];
          } else {
            index = Bytes::get_Java_u2(pc+1);
            incr = 3;
            wide = true;
          }

          ConstantPool* constants = METHOD->constants();
          switch (constants->tag_at(index).value()) {
          case JVM_CONSTANT_Integer:
            SET_STACK_INT(constants->int_at(index), 0);
            break;

          case JVM_CONSTANT_Float:
            SET_STACK_FLOAT(constants->float_at(index), 0);
            break;

          case JVM_CONSTANT_String:
            {
              oop result = constants->resolved_references()->obj_at(index);
              if (result == NULL) {
                CALL_VM(InterpreterRuntime::resolve_ldc(THREAD, (Bytecodes::Code) opcode), handle_exception);
                SET_STACK_OBJECT(THREAD->vm_result(), 0);
                THREAD->set_vm_result(NULL);
              } else {
                VERIFY_OOP(result);
                SET_STACK_OBJECT(result, 0);
              }
            break;
            }

          case JVM_CONSTANT_Class:
            VERIFY_OOP(constants->resolved_klass_at(index)->java_mirror());
            SET_STACK_OBJECT(constants->resolved_klass_at(index)->java_mirror(), 0);
            break;

          case JVM_CONSTANT_UnresolvedClass:
          case JVM_CONSTANT_UnresolvedClassInError:
            CALL_VM(InterpreterRuntime::ldc(THREAD, wide), handle_exception);
            SET_STACK_OBJECT(THREAD->vm_result(), 0);
            THREAD->set_vm_result(NULL);
            break;

          default:  ShouldNotReachHere();
          }
          UPDATE_PC_AND_TOS_AND_CONTINUE(incr, 1);
        }

这个在里面打不了断点呢,上便的逻辑一点都不清楚,如何是好? 这个cpp文件都是灰色的,最后有这个 #ifndef CC_INTERP ,感觉不是模板编译器,

那么,参考知乎的文章,打了断点 发现bytecode 是fast_aldc,并且要加载的字符串是"system",与ldc 指令对上了呢,没想明白怎么出来的一个_fast_ldc指令,对于指令的解析,已经看过了没有对应的Constant String ,tag保存的类型为08的,最后还是让我发现了,hotspot在方法链接的时候将ldc 重写成了_fast_aldc,自己没看方法重写的代码,不知道呢

为什么呢?其实这不怪你,因为这是在连接的时候重写了,自己没看这部分内容,看来还要补起来

// Rewrites a method given the index_map information
void Rewriter::scan_method(Method* method, bool reverse, bool* invokespecial_error) {
    ..
    case Bytecodes::_ldc:
    case Bytecodes::_fast_aldc:  // if reverse=true
        maybe_rewrite_ldc(bcp, prefix_length+1, false, reverse);
    ..
}

// Rewrite some ldc bytecodes to _fast_aldc
void Rewriter::maybe_rewrite_ldc(address bcp, int offset, bool is_wide,
                                 bool reverse) {
    if (!reverse) {
        assert((*bcp) == (is_wide ? Bytecodes::_ldc_w : Bytecodes::_ldc), "not ldc bytecode");
        address p = bcp + offset;
        int cp_index = is_wide ? Bytes::get_Java_u2(p) : (u1)(*p);
        constantTag tag = _pool->tag_at(cp_index).value();
        /**
         *   bool is_method_handle() const { return _tag == JVM_CONSTANT_MethodHandle; }
         *     bool is_method_type() const { return _tag == JVM_CONSTANT_MethodType; }
         *     bool is_string() const { return _tag == JVM_CONSTANT_String; }
         */

        if (tag.is_method_handle() || tag.is_method_type() || tag.is_string()) {

            int ref_index = cp_entry_to_resolved_references(cp_index);
            if (is_wide) {
                (*bcp) = Bytecodes::_fast_aldc_w;
                assert(ref_index == (u2)ref_index, "index overflow");
                Bytes::put_native_u2(p, ref_index);
            } else {
                (*bcp) = Bytecodes::_fast_aldc;//将ldc转化成_fast_aldc,这个你不看到这里,怎么也跟踪不到呢??
                assert(ref_index == (u1)ref_index, "index overflow");
                (*p) = (u1)ref_index;
            }
        }
    } else {
        Bytecodes::Code rewritten_bc =
                (is_wide ? Bytecodes::_fast_aldc_w : Bytecodes::_fast_aldc);
        if ((*bcp) == rewritten_bc) {
            address p = bcp + offset;
            int ref_index = is_wide ? Bytes::get_native_u2(p) : (u1)(*p);
            int pool_index = resolved_references_entry_to_pool_index(ref_index);
            if (is_wide) {
                (*bcp) = Bytecodes::_ldc_w;
                assert(pool_index == (u2)pool_index, "index overflow");
                Bytes::put_Java_u2(p, pool_index);
            } else {
                (*bcp) = Bytecodes::_ldc;
                assert(pool_index == (u1)pool_index, "index overflow");
                (*p) = (u1)pool_index;
            }
        }
    }
}

接着就是

那看ldc和fast_aldc的汇编器,这里面写着生成oop对象

// Fast path for caching oop constants.
void TemplateTable::fast_aldc(bool wide) {
  transition(vtos, atos);

  Register result = rax;
  Register tmp = rdx;
  int index_size = wide ? sizeof(u2) : sizeof(u1);

  Label resolved;

  // We are resolved if the resolved reference cache entry contains a
  // non-null object (String, MethodType, etc.)
  assert_different_registers(result, tmp);
  __ get_cache_index_at_bcp(tmp, 1, index_size);
  __ load_resolved_reference_at_index(result, tmp);
  __ testl(result, result);
  __ jcc(Assembler::notZero, resolved);

  address entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_ldc);

  // first time invocation - must resolve first
  __ movl(tmp, (int)bytecode());
  __ call_VM(result, entry, tmp);

  __ bind(resolved);

  if (VerifyOops) {
    __ verify_oop(result);
  }
}

如下

oop Bytecode_loadconstant::resolve_constant(TRAPS) const {
  assert(_method.not_null(), "must supply method to resolve constant");
  int index = raw_index();
  ConstantPool* constants = _method->constants();
  if (has_cache_index()) {
    return constants->resolve_cached_constant_at(index, THREAD);
  } else {
    return constants->resolve_constant_at(index, THREAD);
  }
}
---------------
  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, 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(constantPoolHandle this_oop, int index, int cache_index, 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.
    assert(index > 0, "valid index");
    cache_index = this_oop->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_oop->resolved_references()->obj_at(cache_index);
    if (result_oop != NULL) {
      return result_oop;
      // That was easy...
    }
    index = this_oop->object_to_cp_index(cache_index);
  }

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

  int tag_value = this_oop->tag_at(index).value();

  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_oop, index, CHECK_NULL);
      // ldc wants the java mirror.
      result_oop = resolved->java_mirror();
      break;
    }

  case JVM_CONSTANT_String:
    assert(cache_index != _no_index_sentinel, "should have been set");
    if (this_oop->is_pseudo_string_at(index)) {
      result_oop = this_oop->pseudo_string_at(index, cache_index);
      break;
    }
    result_oop = string_at_impl(this_oop, index, cache_index, CHECK_NULL);
    break;

  case JVM_CONSTANT_MethodHandleInError:
  case JVM_CONSTANT_MethodTypeInError:
    {
      Symbol* error = SystemDictionary::find_resolution_error(this_oop, index);
      guarantee(error != (Symbol*)NULL, "tag mismatch with resolution error table");
      ResourceMark rm;
      THROW_MSG_0(error, "");
      break;
    }

  case JVM_CONSTANT_MethodHandle:
    {
      int ref_kind                 = this_oop->method_handle_ref_kind_at(index);
      int callee_index             = this_oop->method_handle_klass_index_at(index);
      Symbol*  name =      this_oop->method_handle_name_ref_at(index);
      Symbol*  signature = this_oop->method_handle_signature_ref_at(index);
      if (PrintMiscellaneous)
        tty->print_cr("resolve JVM_CONSTANT_MethodHandle:%d [%d/%d/%d] %s.%s",
                      ref_kind, index, this_oop->method_handle_index_at(index),
                      callee_index, name->as_C_string(), signature->as_C_string());
      KlassHandle callee;
      { Klass* k = klass_at_impl(this_oop, callee_index, CHECK_NULL);
        callee = KlassHandle(THREAD, k);
      }
      KlassHandle klass(THREAD, this_oop->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_oop, index, tag_value, CHECK_NULL);
      }
      break;
    }

  case JVM_CONSTANT_MethodType:
    {
      Symbol*  signature = this_oop->method_type_signature_at(index);
      if (PrintMiscellaneous)
        tty->print_cr("resolve JVM_CONSTANT_MethodType [%d/%d] %s",
                      index, this_oop->method_type_index_at(index),
                      signature->as_C_string());
      KlassHandle klass(THREAD, this_oop->pool_holder());
      Handle value = SystemDictionary::find_method_handle_type(signature, klass, THREAD);
      result_oop = value();
      if (HAS_PENDING_EXCEPTION) {
        save_and_throw_exception(this_oop, index, tag_value, CHECK_NULL);
      }
      break;
    }

  case JVM_CONSTANT_Integer:
    assert(cache_index == _no_index_sentinel, "should not have been set");
    prim_value.i = this_oop->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_oop->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_oop->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_oop->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_oop(), index, cache_index, tag_value) );
    assert(false, "unexpected constant tag");
    break;
  }

  if (cache_index >= 0) {
    // Cache the oop here also.
    Handle result_handle(THREAD, result_oop);
    MonitorLockerEx ml(this_oop->lock());  // don't know if we really need this
    oop result = this_oop->resolved_references()->obj_at(cache_index);
    // Benign race condition:  resolved_references may already be filled in while we were trying to lock.
    // 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.
    // That result may be either a resolved constant or a failure exception.
    if (result == NULL) {
      this_oop->resolved_references()->obj_at_put(cache_index, result_handle());
      return result_handle();
    } else {
      // Return the winning thread's result.  This can be different than
      // result_handle() for MethodHandles.
      return result;
    }
  } else {
    return result_oop;
  }
}
-------------------
oop ConstantPool::string_at_impl(constantPoolHandle this_oop, int which, int obj_index, TRAPS) {
  // If the string has already been interned, this entry will be non-null
  oop str = this_oop->resolved_references()->obj_at(obj_index);
  if (str != NULL) return str;
  Symbol* sym = this_oop->unresolved_string_at(which);
  str = StringTable::intern(sym, CHECK_(NULL));
  this_oop->string_at_put(which, obj_index, str);
  assert(java_lang_String::is_instance(str), "must be string");
  return str;
}
-----
oop StringTable::intern(Symbol* symbol, TRAPS) {
  if (symbol == NULL) return NULL;
  ResourceMark rm(THREAD);
  int length;
  jchar* chars = symbol->as_unicode(length);
  Handle string;
  oop result = intern(string, chars, length, CHECK_NULL);
  return result;
}
-----
oop StringTable::intern(Handle string_or_null, jchar* name,
                        int len, TRAPS) {
  unsigned int hashValue = hash_string(name, len);
  int index = the_table()->hash_to_index(hashValue);
  oop found_string = the_table()->lookup(index, name, len, hashValue);

  // Found
  if (found_string != NULL) return found_string;

  debug_only(StableMemoryChecker smc(name, len * sizeof(name[0])));
  assert(!Universe::heap()->is_in_reserved(name),
         "proposed name of symbol must be stable");

  Handle string;
  // try to reuse the string if possible
  if (!string_or_null.is_null()) {
    string = string_or_null;
  } else {
    string = java_lang_String::create_from_unicode(name, len, CHECK_NULL);
  }

  // Grab the StringTable_lock before getting the_table() because it could
  // change at safepoint.
  MutexLocker ml(StringTable_lock, THREAD);

  // Otherwise, add to symbol to table
  return the_table()->basic_add(index, string, name, len,
                                hashValue, CHECK_NULL);
}
-----
oop StringTable::basic_add(int index_arg, Handle string, jchar* name,
                           int len, unsigned int hashValue_arg, TRAPS) {

  assert(java_lang_String::equals(string(), name, len),
         "string must be properly initialized");
  // Cannot hit a safepoint in this function because the "this" pointer can move.
  No_Safepoint_Verifier nsv;

  // Check if the symbol table has been rehashed, if so, need to recalculate
  // the hash value and index before second lookup.
  unsigned int hashValue;
  int index;
  if (use_alternate_hashcode()) {
    hashValue = hash_string(name, len);
    index = hash_to_index(hashValue);
  } else {
    hashValue = hashValue_arg;
    index = index_arg;
  }

  // Since look-up was done lock-free, we need to check if another
  // thread beat us in the race to insert the symbol.

  oop test = lookup(index, name, len, hashValue); // calls lookup(u1*, int)
  if (test != NULL) {
    // Entry already added
    return test;
  }

  HashtableEntry<oop, mtSymbol>* entry = new_entry(hashValue, string());
  add_entry(index, entry);
  return string();
}

这下下就清楚了,这是气人,你重写代码,怎么不和我说一下呢