zoukankan      html  css  js  c++  java
  • java栈帧中的对象引用

           openjdk中的java栈帧是如何布置的呢,在java栈中如果确定变量是一个引用呢,先复习《深入java虚拟机第二版》有关栈帧的内容。

            “栈帧由三部分组成:局部变量区、操作数栈和栈数据区。局部变量区和操作数栈要视对应的方法而定,他们是按字长计算的。编译器在编译时就确定了这些值并放在class文件中,而栈数据区的大小依赖于具体实现。

              当虚拟机调用一个java方法时,它从对应的类的类型信息得到局部变量区和操作数栈的大小,并据此分配栈帧内存,并压入java栈中。

             局部变量区 java栈帧的局部变量区被组织成一个以字长为单位、从0开始计数的数组。字节码指令通过以0开始的索引来使用其中的数据。类型为int、float、refence和returnAdress的值在数组内只占据一项。而类型为byte、short、char的值在存入数组时都先转换为int值,因此同样只占据一项。而类型为long和double的值在数组中却占据了连续两项。

            局部变量区包含了对应的方法参数和局部变量。”

           现在的问题是在垃圾回收算法遍历java线程堆栈时候,它是如何确定局部变量是基本类型还是对象引用呢?

           openjdk对局部变量区解释有两种方法,通过TaggedStackInterpreter这个设置来区分。从英文意思上面来看应该是一个通过在栈中打上标志,另外一个则是在其他地方做标志。在栈上打标志,则应该是每个变量多压入一个标志头(为一个字长),猜测是否正确呢,看看解释器将引用压栈的代码。

          void BytecodeInterpreter::astore(intptr_t* tos,    int stack_offset,
                              intptr_t* locals, int locals_offset) {
                     if (TaggedStackInterpreter) {
                              frame::Tag t = (frame::Tag) tos[Interpreter::expr_tag_index_at(-stack_offset)];
                              locals[Interpreter::local_tag_index_at(-locals_offset)] = (intptr_t)t;
                     }
                     intptr_t value = tos[Interpreter::expr_index_at(-stack_offset)];
                     locals[Interpreter::local_index_at(-locals_offset)] = value;
           }

          local_index_at代码   

          {
                    return stackElementWords() * i + (value_offset_in_bytes()/wordSize);
           }

           stackElementWords() 代码

          {

                return TaggedStackInterpreter ? 2 : 1; //如果是在栈打标志,字长为2,否则是1,在这可以肯定每个变量前都会多压入一个字长的标志头,虚拟机会通过这个标志头来确定是否是对象引用

           }

          上面分析对象在栈中打标志来区分基本类型和引用类型,这种方法每个变量起码多了一个字的内存空间,空间浪费严重。如果能用位图表示岂不更好?再看另外一种方案。

          从上面的压栈代码看不到另外的操作,openjdk是如何做的呢,还是从上篇gc代码中看看。

          void frame::oops_interpreted_do(OopClosure* f, const RegisterMap* map, bool query_oop_map_cache) {

                    ........

                    jint      bci = interpreter_frame_bci();

                    methodHandle m (thread, interpreter_frame_method());

                    InterpreterFrameClosure blk(this, max_locals, m->max_stack(), f);

                    InterpreterOopMap mask;
                    if (query_oop_map_cache) { //传入true
                         m->mask_for(bci, &mask);  //从oopMapCache中填充mask变量
                    } else {
                         OopMapCache::compute_one_oop_map(m, bci, &mask);
                    }
                   mask.iterate_oop(&blk);

         }

         void InterpreterOopMap::iterate_oop(OffsetClosure* oop_closure) {
                   int n = number_of_entries();
                   int word_index = 0;
                   uintptr_t value = 0;
                   uintptr_t mask = 0;
                   // 遍历每项
                   for (int i = 0; i < n; i++, mask <<= bits_per_entry) {
                            if (mask == 0) {
                                    value = bit_mask()[word_index++];
                                    mask = 1;
                            }
                           // 判断是否是对象引用?
                           if ((value & (mask << oop_bit_number)) != 0) oop_closure->offset_do(i);
                  }
          }

         void InterpreterFrameClosure::offset_do(int offset) {

                  oop* addr;
                  if (offset < _max_locals) {
                         addr = (oop*) _fr->interpreter_frame_local_at(offset);
                         assert((intptr_t*)addr >= _fr->sp(), "must be inside the frame");
                         _f->do_oop(addr);  //进行标记和压栈
                  } 
         }
         从上面来看,采用位图来区分对象引用基本上是肯定的,具体如何实现还有待于细读。

  • 相关阅读:
    leetcode——202.快乐数
    leetcode——136. 只出现一次的数字
    leetcode——74.搜索二维矩阵
    leetcode——240.搜索二维矩阵
    leetcode——414.第三大的数
    020 迭代器模式
    019 模板模式
    018 中介者模式
    017 命令模式
    016 责任链模式
  • 原文地址:https://www.cnblogs.com/dongxiaoguang/p/2962030.html
Copyright © 2011-2022 走看看