葛一鸣 jvm实战

jvm是如何解析参数的

对于每个+XX参数(比如PrintGC),都会调用到arguments.cpp的parse_argument方法,然后jvm回去找之前定义过的Flag(大概有1120左右个FLAG,比如PrintGC), 

也即是find_flag方法,如果找到了返回Null,否则返回一个bool值

具体处理在boolAtPut,根据查找结果返回true或者false,并且给指定flag设置值(避免每次都执行查找)

现在碰到的问题就是- XX: +DoEscapeAnalysis 这个参数,jvm报找不到

方法真正调用的地方


int JNICALL
JavaMain(void * _args)是最开始的方法,首先会create_jvm,然后创建一个新线程去loadMainClass

loadMain以后会执行方法
    mainID = (*env)->GetStaticMethodID(env, mainClass, "main",
                                       "([Ljava/lang/String;)V");

来执行static方法,这个是一个JNI接口,具体内容如下:


JNI_ENTRY(jmethodID, jni_GetStaticMethodID(JNIEnv *env, jclass clazz,
          const char *name, const char *sig))
  JNIWrapper("GetStaticMethodID");
#ifndef USDT2
  DTRACE_PROBE4(hotspot_jni, GetStaticMethodID__entry, env, clazz, name, sig);
#else /* USDT2 */
  HOTSPOT_JNI_GETSTATICMETHODID_ENTRY(
                                      env, (char *) clazz, (char *) name, (char *)sig);
#endif /* USDT2 */
  jmethodID ret = get_method_id(env, clazz, name, sig, true, thread);
#ifndef USDT2
  DTRACE_PROBE1(hotspot_jni, GetStaticMethodID__return, ret);
#else /* USDT2 */
  HOTSPOT_JNI_GETSTATICMETHODID_RETURN(
                                       (uintptr_t) ret);
#endif /* USDT2 */
  return ret;
JNI_END

注意进入到get_method_id方法:

 其中sig就是方法的签名字符串,然后生成的klass对象会lookup_method去寻找对应签名的方法

Method* lookup_method(Symbol* name, Symbol* signature) const {
    return uncached_lookup_method(name, signature, find_overpass);
  }

而真正执行方法的是:

(*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);

然后这个方法会调用:

jni_invoke_static(env, &jvalue, NULL, JNI_STATIC, methodID, &ap, CHECK);

然后

static void jni_invoke_static(JNIEnv *env, JavaValue* result, jobject receiver, JNICallType call_type, jmethodID method_id, JNI_ArgumentPusher *args, TRAPS) {
  methodHandle method(THREAD, Method::resolve_jmethod_id(method_id));

  // Create object to hold arguments for the JavaCall, and associate it with
  // the jni parser
  ResourceMark rm(THREAD);
  int number_of_parameters = method->size_of_parameters();
  JavaCallArguments java_args(number_of_parameters);
  args->set_java_argument_object(&java_args);

  assert(method->is_static(), "method should be static");

  // Fill out JavaCallArguments object
  args->iterate( Fingerprinter(method).fingerprint() );
  // Initialize result type
  result->set_type(args->get_ret_type());

  // Invoke the method. Result is returned as oop.
  JavaCalls::call(result, method, &java_args, CHECK);

实际调用的是JavaCalls的call,进一步调用call_helper 

call_helper里面真正docall的方法就是:

StubRoutines::call_stub()(
        (address)&link,
        // (intptr_t*)&(result->_value), // see NOTE above (compiler problem)
        result_val_address,          // see NOTE above (compiler problem)
        result_type,
        method(),
        entry_point,
        args->parameters(),
        args->size_of_parameters(),
        CHECK
      );

在stubGeneratir_zero的方法里面

这个方法会setup栈,并且最终进入

Interpreter::invoke_method(method, entry_point, THREAD);

 需要注意的是invoke_method,是不是反射的invoke方法呢? 看起来很像:

实际调用到entry_point(这就是一个方法的入口点)的invoke,结果后面就是linkResolver.cpp? 咋还要解析????? 调用栈如下,其实最终调到的是call_virtual

 结果call_virtual也是调call,艹:


void JavaCalls::call_virtual(JavaValue* result, KlassHandle spec_klass, Symbol* name, Symbol* signature, JavaCallArguments* args, TRAPS) {
  CallInfo callinfo;
  Handle receiver = args->receiver();
  KlassHandle recvrKlass(THREAD, receiver.is_null() ? (Klass*)NULL : receiver->klass());
  LinkResolver::resolve_virtual_call(
          callinfo, receiver, recvrKlass, spec_klass, name, signature,
          KlassHandle(), false, true, CHECK);
  methodHandle method = callinfo.selected_method();
  assert(method.not_null(), "should have thrown exception");

  // Invoke the method
  JavaCalls::call(result, method, args, CHECK);
}

然后执行字节码

JvmtiHideSingleStepping jhss(thread);
    LinkResolver::resolve_invoke(info, receiver, pool,
                                 get_index_u2_cpcache(thread, bytecode), bytecode, CHECK);
    i 

那么问题来了,在parseClassFile的时候也会进到这个方法,靠!

关键还是,这个Method结构体,我看不懂,全tm是字符。。。

现在把

初始化java system的时候是如下:

build方法用来构建栈帧,后面就是执行字节码了,关于栈和栈帧,参考滑动验证页面

关注一下stack_words = max_stack =1,这个是方法的最大栈深度,如果构造器方法,其实max_stack至少是2,因为要调用父类Object的初始化方法(invokesepcial指令)

max_locals就是这个方法的局部变量总数

 参数参见 Chapter 4. The class File Format

有一个

locals = stack->sp() + (method->max_locals() - 1);

有点好奇为啥locals就是sp+ max_locals,难道metho最开始就是local局部变量?

 然后就是while循环执行字节码:

走进了ByteCode的run方法

 其中有个bcp

其实就是

// 获取解释器当前的(B)yte (C)ode (P)ointer,也就是当前指令地址,以指针表达
static address   bcp(JavaThread *thread)           { 
  return last_frame(thread).interpreter_frame_bcp(); 
}

然后因为是方法,会走到method_entry里面:

set_do_not_unlock方法,好像是为了synchronize字段的

如果是synchronize方法,会特殊处理 ,后面会释放unlock

 接下来goto run:

先是pc自增,然后会根据指令,来获取索引,

 然后CALL_VM的时候,会进入入口点 

 

接下来进入resolve_invoke

后面就到了LinkResolve的resolve_static 

 resolve_static_call会根据method来生成一个KlassHandle实例,

 然后会调用set_static方法:

 set_common方法:

 问题是,这到底解析了个啥? 我也不知道

不过method的signature是个Symbol对象

 InstanceKlass:每个java类都对应一个InstanceKlass ,存储类的元信息,具体布局看下图

可以看到InstanceKlass里面有annotation,fields, 其实下面还有methods,是个数组

 接下来进入cache_entry方法:

 最后会返回一个ConstantPoolCacheEntry指针:

 JVM符号引用转换直接引用的过程? - 知乎

首先会bias锁校验,看是不是biased_lock_pattern,一般来说是1(unlocked_value)


  enum { locked_value             = 0,
         unlocked_value           = 1,
         monitor_value            = 2,
         marked_value             = 3,
         biased_lock_pattern      = 5
  };

  跑完以后会,走到代码 goto run,然后又开始增加pc计数,如下图:

 run的时候会根据opcode执行指定操作,如下: 

关于java的mirror,可看上图及下图,上图可以看出,静态方法其实就是取对应的java mirror,也就是class实例,如果非静态方法,就取LOCAL_OBJECT(0)

第6.1篇-方法解析之Method与ConstMethod介绍 - 鸠摩(马智) - 博客园

jvm的method

TLAB 区别于栈内存分配,是为每个线程分配一个很小的私有堆,默认开启

浅堆和深堆,实际的大小

浅堆可以理解对象引用的大小,基本都很小,比如一个int就8个字节,一个String24个字节

深堆就是gc以后能释放的空间

偏向锁最后两位固定为01

cinit是class的初始化(比如初始化静态变量),init是实例的初始化(比如new A())

final变量是准备阶段(准备阶段就是分配内存,并且初始化为默认值,类似于memset)就赋值,非final就是在初始化阶段赋值

解析就是:将类接口、宇段和方法的符号引用转为直接引用

初始化:就是调用clinit方法(会执行非final静态变量和static代码块的语句) 多线程可能会死锁

8.2.2轻量级锁 281对应的源码看一下,8.2.2后面也没看

8.3看了

第10章看完了

5.5看到了5.5.4

第11章11.5及往后都看完了,除了11.5.4

热替换,就是不断轮询,看class文件有没有更新的过程

双亲委派的弊端,就是父类无法访问子类加载的类,作为一种补充,可以通过SPI机制,以及Thread的currentThread的getContextLoader,这样父类就能访问子类加载的类了

另一种打破双亲委派机制的方法是: 重写loadClass方法,大概率也要同时重写loadClass内部的findClass方法,参考:

ClassLoader笔记_newbaby2012的博客-CSDN博客

Arthas学习

jad 反编译

dashboard 看板

thread <pid> 查看耗费cpu的代码在哪一行

锁分离,是一种减小锁粒度的特例,比如LinkedBlockingQueue,就是入队

和出队两个锁,而不是锁整个队列,读写就有更好的并发

Full GC 自适应策略

Full GC (Ergonomics) 产生的原因_→思的博客-CSDN博客_ergonomics jvm

G1收集器总结

G1是Garbage First Garbage Collector的简称,其实会把堆分成很多个小的区域:S代表survivor,E代表Eden(伊甸园,人类开始居住的地方,也即对象刚开始所在的地方),O代表Old老年代,G就代表Garbage(垃圾占比比较高的区域,优先收集G)

那么G类区域是如何生成的呢?我猜是并发标记周期的时候,给标上的G(猜对了hhh),而这些G型区域会被记录在Collection Set中

G1收集的4个阶段:新生代GC, 并发标记周期,混合收集(年轻代,老年代都会回收),FULL GC

并发标记周期是G1最重要的一个阶段,分很多,但我不想写,截个图吧

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值