最近项目出了一个线上问题,问题情况是这样子的,我在类CS中声明了一个静态的布尔值A,默认值为false,为了保证主存读取一致,给它加了个volatile的修饰符,当执行时根据此值是否为false去执行初始化操作,然后再将初始化结果返回给A.
在一般的设备上运行是一切正常的,直到我遇到一台MI PAD2 ( 5.1,x86_64),在这个设备上当我new 好CS类的一个对象后,布尔值神奇的默认值变为true了!
我继续分析,值变了true是在类实例化时变的,我在类的static中添加了一个输出,类被加载的时候还是为false的,但在构造方法中已经变成了true。当前的解决办法是把volatile移除就没问题了,所以我是怀疑volatile这个修饰符引起了问题。
后来我通过分析得出了以下重现的条件:
1. 类CS需要在非系统classLoader中加载;
2. 类CS需要extends或者implements非当前ClassLoader中的类;
3. 类CS需要包含volatile的成员;
4. 成员变量为boolean,且初始值为false;
达成以上条件,运行时即可以触发,目前已知以下情况不会引起问题:
1. 类当中包含其他ClassLoader已加载的类;
2. 其他类型的成员不会引起这样的问题;
3. boolean为true则一直为true;
4. 实例化时才会触发;
5. 在运行过程中将其值设置为与默认值相反的值,都不会影响最后的取值,都为true;
6. 类加载过程中静态区初始化时将其值取反,仅会影响到static{}块中的值,实例化时又会被置为true;
由于volatile属于一个JIT范畴的一个修饰符,但JIT有可能在不同的设备上会进行不同的优化操作,不知是否在这个设备上的JIT是不是出了问题,而且JIT也跟具体的CPU平台有关,会将其转换为汇编指令,所以重现的概率也不大。