欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

为什么我的对象被 IntelliJ IDEA 悄悄的修改了?

程序员文章站 2022-06-06 22:18:41
背景     最近,在复习JUC的时候调试了一把ConcurrentLinkedQueue的 方法,意外的发现Idea在debug模式下竟然会 “自动修改” 已经创建的Java对象,当时觉得这个现象很是奇怪,现在把问题的原因以及解决过程记录下来,希望你在调试的 ......

背景

    最近,在复习juc的时候调试了一把concurrentlinkedqueue的offer方法,意外的发现idea在debug模式下竟然会 “自动修改” 已经创建的java对象,当时觉得这个现象很是奇怪,现在把问题的原因以及解决过程记录下来,希望你在调试的时候不要踩坑。

调试代码

    调试的代码很简单,就是多次调用offer方法,然后观察concurrentlinkedqueue的headtail属性。

import java.lang.reflect.field;
import java.util.concurrent.concurrentlinkedqueue;

public class concurrentlinkedqueuetest {

    public static void main(string[] args) {
        concurrentlinkedqueue<string> queue = new concurrentlinkedqueue<>();
        print(queue);
        queue.offer("aaa");
        print(queue);
        queue.offer("bbb");
        print(queue);
        queue.offer("ccc");
        print(queue);
    }

    /**
     * 打印并发队列head属性的identityhashcode
     * @param queue
     */
    private static void print(concurrentlinkedqueue queue) {
        field field = null;
        boolean isaccessible = false;
        try {
            field = concurrentlinkedqueue.class.getdeclaredfield("head");
            isaccessible = field.isaccessible();
            if (!isaccessible) {
                field.setaccessible(true);
            }
            system.out.println("head: " + system.identityhashcode(field.get(queue)));
        } catch (exception e) {
            e.printstacktrace();
        } finally {
            field.setaccessible(isaccessible);
        }
    }
}

调试过程

    上述代码在idea中debug模式下head属性会无缘无故的被修改(run模式下正常,debug模式下关闭所有断点也正常),检查concurrentlinkedqueue的源码发现,head属性只有在构造器和反序列化的readobject共3处地方才会被直接赋值(不是cas修改),我也仔细检查了offer方法,确实没有修改head的地方。而idea在debug时,把head属性修改为第一次offer的node节点,这个现象就很奇怪了。

  • 在run模式下的输出结果,多次调用offer方法,head属性都是同一个对象(debug模式下关闭所有断点也是同样的效果)
    为什么我的对象被 IntelliJ IDEA 悄悄的修改了?

  • 在offer方法中断点,然后debug并单步调试(step over)
    为什么我的对象被 IntelliJ IDEA 悄悄的修改了?

  • 在offer方法中断点,然后debug并直接运行到下一个断点(resume program)
    为什么我的对象被 IntelliJ IDEA 悄悄的修改了?

    由上可见,在debug进入offer方法之后head属性确实被修改了(对象已经不是同一个),而且这不是偶尔出现,而是一直可以复现的,step over和resume program也表现出了修改head属性不同的时机,这让人很费解。 更费解的是就算不在offer方法体里断点,在main方法中断点也会出现head被修改的现象。

  • 转战到eclipse,同样的环境,同样的操作,在run和debug模式下都不会出现head被修改的情况
    为什么我的对象被 IntelliJ IDEA 悄悄的修改了?

分析

    了解到idea在debug模式下默认开启了tostring预览特性(settings>>build,execution,deployment>>debugger>>data views>>java>>enable 'tostring()' object view),可是调用tostring方法也不至于把对象本身都修改了啊,也专门看了下concurrentlinkedqueue的内部类node,并没有复写tostring方法(事后回顾,当时在这里疏忽了,下文会再介绍),但还是关掉特性再测试一遍,然而还是同样的结果,head属性任然被悄悄的修改了。第二天来到公司在同事的环境(intellij idea 2019.1)上验证了下,还是同样的问题,排除idea版本的因素。

    郁闷了一会儿,就向"网友"提问了,不久就得到了intellij idea的产品经理yole的回复,他的意思还是idea 的 data views 的 tostring 在作怪,上文已经说过关掉tostring特性还是有这个问题,但是他给了我一个重要的思路就是:在debug模式下,concurrentlinkedqueue的对象也会被调用tostring方法的,在队列的tostring方法中会获取队列的迭代器,而创建迭代器时会调用first方法,first方法里就会cas修改head属性。(之前确实没考虑到队列本身的tostring方法,而是去看node是否重写了tostring,手动哭脸