`
xieyj
  • 浏览: 103122 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

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);  //进行标记和压栈
              }
     }
     从上面来看,采用位图来区分对象引用基本上是肯定的,具体如何实现还有待于细读。

2
0
分享到:
评论
2 楼 Mr_lee_2012 2011-12-08  
是啊,好文章,谢过。
1 楼 mercyblitz 2008-09-14  
这么好的文章,居然没有人留言

相关推荐

    Java虚拟机栈--栈帧.docx

    变量槽可以存放基本数据类型(如int, float等)、对象引用以及returnAddress类型。对于32位的数据类型,每个槽位可以存放一个变量;而64位的数据类型(如long和double)则需要两个槽位。变量槽的位置与方法参数和...

    java程序中的内存分配问题

    2. **栈内存**:栈主要用于存储方法的局部变量,包括基本类型和对象引用。每当一个方法被调用,都会在栈上创建一个新的栈帧,用于存放局部变量、操作数栈和方法返回地址等信息。当方法执行完毕,栈帧就会被弹出,其...

    java实现内存动态分配

    堆内存主要用来存储对象实例和数组,而栈内存主要存储基本类型变量和对象引用。 2. **堆内存分配** 堆内存是Java中的全局共享区域,用于存储所有的对象实例和数组。当通过`new`关键字创建一个新的对象时,其实际的...

    动力节点_Java零基础_课堂笔记.zip

    3. **this本质**:`this`关键字在Java中表示当前对象的引用,它常用于区分成员变量和局部变量,或者在构造方法中调用其他构造方法。 4. **OOTest**:OOTest可能是指对象和类(Object-Oriented Testing)的测试,...

    java中方法重载内存分析

    栈内存主要用于存储基本类型变量(如int、char)和对象引用,而堆内存则用于存储对象实例。 当一个方法被调用时,JVM会在栈内存中创建一个栈帧(Stack Frame),这个栈帧包含了局部变量表、操作数栈、动态链接和...

    java内存分配情况

    - **栈内存共享**:栈中的基本类型变量和对象引用是按值存储的。如果多个变量引用同一个对象,它们实际上共享同一份堆内存中的对象,改变一个引用变量的值不会影响其他引用变量。 了解这些内存分配规则有助于避免...

    java中带有this关键字的程序内存分析

    首先,`this`关键字在Java中主要表示当前对象的引用。它可以在类的方法中使用,用来访问该类的实例变量,调用其他方法,或者在构造器中传递当前对象的引用。当在类的方法或构造器中使用`this`时,它指代的就是正在...

    【IT十八掌徐培成】Java基础第04天-03.OOP-栈区-堆区-引用.zip

    首先,栈区是Java内存模型中的一个重要部分,它主要用于存储基本类型变量(如int、float、boolean等)和对象引用。栈内存分配速度快,因为它的操作主要是基于固定大小的数据单元。当一个方法被调用时,一个新的栈帧...

    Java中堆内存和栈内存详解

    - **对象引用**:为了访问堆内存中的对象,可以在栈内存中创建一个引用变量,该变量的值为对象在堆内存中的地址。 - **垃圾回收**:Java虚拟机(JVM)会自动管理堆内存,通过垃圾回收机制(Garbage Collection, GC)...

    JAVA 内存管理总结

    通过对象引用,我们可以访问堆内存中的对象数据。 **对象实例化过程**: 1. 当执行 `new Rectangle(3,5)`,系统首先在堆内存中为Rectangle类的实例分配空间,初始化成员变量为默认值。 2. 接着,根据类定义的初始化...

    详解Java 中程序内存的分析

    在Java编程语言中,了解程序内存的管理对于优化...开发者应该注意合理使用栈和堆,避免创建不必要的大对象,及时解除不再使用的对象引用,以确保程序的高效运行。此外,理解垃圾收集机制也是确保内存有效管理的关键。

    Java内存分配全面解析

    对于基本类型(如int)的变量,它们的值直接存储在栈中,而引用类型的变量则在栈中存储对堆中对象的引用。 在方法调用中,局部变量会根据需要在栈中创建和销毁。例如,当传递一个对象作为参数时,实际上传递的是...

    深入理解Java虚拟机笔记(带目录).docx

    * 强引用(Strong Reference):普通的对象引用,垃圾收集器不回收。 * 软引用(Soft Reference):对象的软引用,垃圾收集器在内存不足时回收。 * 弱引用(Weak Reference):对象的弱引用,垃圾收集器在执行时回收...

    Java内存分配浅析

    例如,当创建一个类的实例时,JVM会在堆中分配空间,然后在栈中存储一个指向该对象的引用。对于基本类型的变量,它们的值直接存储在栈中;而对于引用类型的变量,栈中存储的是指向堆中对象的指针。 在方法调用过程...

    变量在Java栈、堆内存中的运用管理分析.pdf

    栈内存主要用于存储基本数据类型变量和对象引用。当在方法内部声明一个变量时,Java会在栈中为其分配空间,存放变量的值。基本数据类型包括boolean、byte、char、int、short、long、float和double,它们的值直接存储...

    《面向对象技术与方法》07、类与对象.pdf

    - **对象与引用的关系**:Java中,程序员只能通过引用去访问或操作对象。这意味着,即使创建了一个对象,如果没有相应的引用指向它,那么这个对象就无法被访问,进而可能会被垃圾回收机制清理掉。 - **引用的特点**...

    Java当中的内存分配.pdf

    数组在Java中也是一种对象,数组的引用存储在栈中,而数组对象本身则存储在堆中。数组对象包含了数组元素的值。 例如: ```java int[] x = new int[3]; ``` 上面的代码创建了一个数组对象,这个对象有三个元素,...

    Java中堆内存和栈内存详解文.pdf

    在Java中,引用变量是一个普通变量,存储在栈中,当其作用域结束时,栈中的引用变量会被释放,但堆内存中的对象仍然存在,直到GC将其回收。 例如,在下面的代码中: ```java public class ArrayTest { public ...

    第五讲-Java对象运行机制与多线程2019-V31

    在JVM中,方法调用通过操作数栈进行参数传递,例如`b.deposit(100)`会将`b`对象引用和`100`推入操作数栈,然后调用`deposit`方法。方法调用结束后,栈帧会被弹出,程序计数器更新,执行下一条指令。 总结来说,Java...

Global site tag (gtag.js) - Google Analytics