《JVM G1源码分析和调优》 —2.6.3 JVM本地方法栈中的对象

举报
华章计算机 发表于 2019/12/20 13:31:54 2019/12/20
【摘要】 本节书摘来自华章计算机《JVM G1源码分析和调优》 一书中第2章,第2.6.3节,作者是彭成寒。

2.6.3 JVM本地方法栈中的对象

上节介绍本地方法栈是如何管理和链接对象的。每一个Java线程都私有一个句柄区_handle_area来存储其运行过程中创建的临时对象,这个句柄区是随着Java线程的栈帧变化的,我们看一下HandleMark是如何管理的。HandleArea的作用上一节已经介绍过了,这里我们先看一下它们的结构图(如图2-2所示),然后再通过代码演示如何管理句柄。

 image.png

图2-2 句柄结构图

Java线程每调用一个Java方法就会创建一个对应HandleMark来保存已分配的对象句柄,然后等调用返回后即行恢复,代码如下所示:

hotspot/src/share/vm/runtime/javaCalls.cpp

 

JavaCalls::call_helper(JavaValue* result, methodHandle* m, JavaCallArguments*

  args, TRAPS) {

  ……

 

  {

  HandleMark hm(thread);

所以当Java线程运行一段时间之后,通过HandleMark构建的对象识别链如图2-3所示:

 image.png

图2-3 本地方法栈对象的管理

这里Chunk的管理是动态变化的,第一个Chunk可能为256或者1024个字节,每一个Chunk都有一个额外空间,主要是调用malloc时会有一段额外的信息,比如地址的长度等,在32位机器上一般为20个字节,所以每一个Chunk都会比最大值少5个OOP对象。另外,一般的Chunk块通常为32KB。最后还需要提一点的就是,Handle

Mark通常都是分配在线程栈中,也意味着无需额外的管理,只需要找到HandleMark就能找到哪些对象是存活的。我们来看一个简单的例子,看看如何遍历堆空间。

下面这个代码片段是为了输出堆空间里面的对象,例如我们执行jmap命令来获取堆空间对象的时候最终会调用到VM_HeapDumper::do_thread()来遍历所有的对象。通过下面的代码我们能非常清楚地看到,如果JavaThread执行的是Java代码,则直接通过StackValueCollection访问局部变量,如果执行的是本地代码,线程则通过active_handles()访问句柄而访问对象。

hotspot/src/share/vm/services/heapDumper.cpp

 

int VM_HeapDumper::do_thread(JavaThread* java_thread, u4 thread_serial_num) {

  JNILocalsDumper blk(writer(), thread_serial_num);

 

  oop threadObj = java_thread->threadObj();

 

  int stack_depth = 0;

  if (java_thread->has_last_Java_frame()) {

 

    Thread* current_thread = Thread::current();

    ResourceMark rm(current_thread);

    HandleMark hm(current_thread);

 

    RegisterMap reg_map(java_thread);

    frame f = java_thread->last_frame();

    vframe* vf = vframe::new_vframe(&f, &reg_map, java_thread);

    frame* last_entry_frame = NULL;

    int extra_frames = 0;

 

    if (java_thread == _oome_thread && _oome_constructor != NULL) {

      extra_frames++;

    }

    while (vf != NULL) {

      blk.set_frame_number(stack_depth);

      if (vf->is_java_frame()) {

 

        // Java线程栈,包括(interpreted, compiled, ...)

        javaVFrame *jvf = javaVFrame::cast(vf);

        if (!(jvf->method()->is_native())) {

          StackValueCollection* locals = jvf->locals();

          for (int slot=0; slot<locals->size(); slot++) {

            if (locals->at(slot)->type() == T_OBJECT) {

              oop o = locals->obj_at(slot)();

 

              if (o != NULL) {

                writer()->write_u1(HPROF_GC_ROOT_JAVA_FRAME);

                writer()->write_objectID(o);

                writer()->write_u4(thread_serial_num);

                writer()->write_u4((u4) (stack_depth + extra_frames));

              }

            }

          }

        } else {

          // 本地方法栈

          if (stack_depth == 0) {

            java_thread->active_handles()->oops_do(&blk);

          } else {

            if (last_entry_frame != NULL) {

              last_entry_frame->entry_frame_call_wrapper()->handles()->

                oops_do(&blk);

            }

          }

        }

        stack_depth++;

        last_entry_frame = NULL;

 

      } else {

        frame* fr = vf->frame_pointer();

        assert(fr != NULL, "sanity check");

        if (fr->is_entry_frame()) {

          last_entry_frame = fr;

        }

      }

      vf = vf->sender();

    }

  } else {

    java_thread->active_handles()->oops_do(&blk);

  }

  return stack_depth;

}


【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。