JVM源码分析之不保证顺序的Class.getMethods

本文探讨了JVM中Class.getMethods方法返回的方法列表不保证顺序的原因,及其可能导致的问题,如对象序列化和反序列化时的顺序依赖。JVM为提高查找效率对方法进行排序,但排序依据是方法名对应的Symbol对象地址,而非字母顺序。解决方案包括预先排序或统一使用特定方法。
摘要由CSDN通过智能技术生成

概述

本文要说的内容是今天公司有个线上系统踩了一个坑,并且貌似还造成了一定的影响,后来系统相关的人定位到了是java.lang.Class.getMethods返回的顺序可能不同机器不一样,有问题的机器和没问题的机器这个返回的方法列表是不一样的,后面他们就来找到我求证是否jdk里有这潜规则

本来这个问题简单一句话就可以说明白,所以在晚上推送的消息里也将这个事实告诉了大家,大家知道就好,以后不要再掉到坑里去了,但是这个要细说起来其实也值得一说,于是在消息就附加了征求大家意见的内容,看大家是否有兴趣或者是否踩到过此坑,没想到有这么多人响应,表示对这个话题很感兴趣,并且总结了大家问得最多的两个问题是

  • 为什么有代码需要依赖这个顺序

  • jvm里为什么不保证顺序

那这篇文章主要就针对这两个问题展开说一下,另外以后针对此类可写可不写的文章先征求下大家的意见再来写可能效果会更好点,一来可以回答大家的一些疑问(当然有些问题我也可能回答不上来,不过我尽量去通读代码回答好大家),二来希望对我公众号里的文章继续保持不求最多,只求最精的态度。

为了不辜负大家的热情,我连夜赶写了这篇文章,如果大家觉得我写的这些文章对大家有帮助,希望您能将文章分享出去,同时将我的公众号你假笨推荐给您身边更多的技术人,能帮助到更多的人去了解更多的细节,在下在此先谢过。

依赖顺序的场景

如果大家看过或者实现过序列化反序列化的代码,这个问题就不难回答了,今天碰到的这个问题其实是发生在大家可能最常用的fastjson库里的,所以如果大家在使用这个库,请务必检查下你的代码,以免踩到此坑

对象序列化

大家都知道当我们序列化好一个对象之后,要反序列回来,那问题就来了,就拿这个json序列化来说吧,我们要将对象序列化成json串,那意味着我们要先取出这个对象的属性,然后写成键值对的形式,那取值就意味着我们要遵循java bean的规范通过getter方法来取,那其实getter方法有两种,一种是boolean类型的,一种是其他类型的,如果是boolean类型的,那我们通常是isXXX()这样的方法,如果是其他类型的,一般是getXXX()这样的方法。那假如说我们的类里针对某个属性a,同时存在两个方法isA()getA(),那究竟我们会调用哪个来取值?这个就取决于具体的序列化框架实现了,比如导致我们这篇文章诞生的fastjson,就是利用我们这篇文章的主角java.lang.Class.getMethods返回的数组,然后挨个遍历,先找到哪个就是哪个,如果我们的这个数组正好因为jvm本身实现没有保证顺序,那么可能先找到isA(),也可能先找到getA(),如果两个方法都是返回a这个属性其实问题也不大,假如正好是这两个方法返回不同的内容呢?

private A a;public A getA(){
       return a;
}public boolean isA(){
       return false;
}public void setA(A a){
       this.a=a;
}

如果是上面的内容,那可能就会悲剧了,如果选了isA(),那其实是返回一个boolean类型的,将这个boolean写入到json串里,如果是选了getA(),那就是将A这个类型的对象写到json串里

对象反序列化

在完成了序列化过程之后,需要将这个字符串进行反序列化了,于是就会去找json串里对应字段的setter方法,比如上面的setA(A a),假如我们之前选了isA()序列化好内容,那我们此时的值是一个boolean值false,那就无法通过setA来赋值还原对象了。

解决方案

相信大家看完我上面的描述,知道这个问题所在了,要避免类似的问题,方案其实也挺多,比如对方法进行先排序,又比如说优先使用isXXX()方法,不过这种需要和开发者达成共识,和setter要对应得起来

jvm里为什么不保证顺序

JDK层面的代码我就暂时不说了,大家都能看到代码,从java.lang.Class.getMethods一层层走下去,相信大家细心点还是能抓住整个脉络的,我这里主要想说大家可能比较难看到的一些实现,比如JVM里的具体实现

正常情况下大家跟代码能跟到调用了java.lang.Class.getDeclaredMethods0这个native方法,其具体实现如下

JVM_ENTRY(jobjectArray, JVM_GetClassDeclaredMethods(JNIEnv *env, jclass ofClass, jboolean publicOnly))
{
   
  JVMWrapper("JVM_GetClassDeclaredMethods");  return get_class_declared_methods_helper(env, ofClass, publicOnly,                                           /*want_constructor*/ false,
                                           SystemDictionary::reflect_Method_klass(), THREAD);
}
JVM_END
其主要调用了`get_class_declared_methods_helper`方法

static jobjectArray get_class_declared_methods_helper(
                                  JNIEnv *env,
                                  jclass ofClass, jboolean publicOnly,
                                  bool want_constructor,
                                  Klass* klass, TRAPS) {
   

  JvmtiVMObjectAllocEventCollector oam;  // Exclude primitive types and array types
  if (java_lang_Class::is_primitive(JNIHandles::resolve_non_null(ofClass))
      || java_lang_Class::as_Klass(JNIHandles::resolve_non_null(ofClass))->oop_is_array()) 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HeapDump性能社区

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值