测不准原理?记一次Guava队列问题的排查-阿里云开发者社区

开发者社区> 北征> 正文

测不准原理?记一次Guava队列问题的排查

简介: ## 引子 测不准原理中有一个现象:人们对光子的观测行为本身会影响观测的结果。近期在排查问题中也遇到了类似的“诡异”问题,初期百思不得其解,真相大白之后摇头苦笑,记在这里贻笑大方。 ## 现象 先看一段经过简化的代码。 ```java // 对象结构体 public class Foo { private int id; public Foo(int i
+关注继续查看

引子

测不准原理中有一个现象:人们对光子的观测行为本身会影响观测的结果。近期在排查问题中也遇到了类似的“诡异”问题,初期百思不得其解,真相大白之后摇头苦笑,记在这里贻笑大方。

现象

先看一段经过简化的代码。

// 对象结构体
public class Foo {
    private int id;

    public Foo(int i) {
        this.id = i;
    }

    public Foo() {

    }

    public void setId(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }
}

// 处理逻辑
List<Foo> fooList = new ArrayList<>();
fooList.add(new Foo(1));
    
List<Foo> newFooList=Lists.transform(fooList,new Function<Foo, Foo>(){
    @Override
    public Foo apply(Foo input){
        Foo output=new Foo();
        // 一些处理逻辑
        output.setId(input.getId()*10);
        return output;
        }
});

for(Foo foo:newFooList){
    // 后处理逻辑
    int id=foo.getId()*10;
    foo.setId(id);
}

问题:最后newFooList里面元素的id是多少?

上面的代码不复杂,JAVA又有强大趁手的调试工具,连上debugger单步走一遭就是。可是结果却令人惊讶:虽然每一行代码都走到了,最后的结果却是明白无误的10——中间的那次乘10操作被吃了?更诡异的事情在后面:如果在for循环里加一个断点,明白无误的看到新的id被赋值进去,但是把鼠标移到newFooList上,里面的指向的object对象里的值还是10,且每次看这个对象的地址都在不断递增,好像后台在不断的new对象,有内存泄露?

分析

对象的地址递增可以解释为何后处理逻辑的值设不进list里:看到的对象已经不是当初的保存对象了,当然看不到设置的值了。apply函数中有new更是高度怀疑对象。

为何list里保存对象会变呢?搂了一遍Guava list的源码,里面自己实现了一个list以及相应的listIterator,只要有对数组元素的查询遍历操作,就会调用apply函数,执行apply函数里的逻辑——也就是每次new一个新对象,且设id的值为10。

为何用idea查看时,断点没动,对象的地址在递增?这里估计是idea做了特殊处理:它会调用list的get方法,重新new对象并设初值,但是不会触发里面的断点。证据就是如果在里面加上print函数,虽然没有触发断点,但是有日志打印出来。

总结

这个问题的产生的根源就是对list.transform的行为逻辑想当然了,以为apply函数的用处是一次性的。虽然java8中已经提供了替代的stream,但是在一些老系统中仍然存在着guava的代码,如果对实现原理理解不清楚就会踩坑。在上述例子中,要注意List.transform后不要再对元素进行处理,如果一定要处理,需要将结果固定到一个数组中(使用Lists.newArrayList()或者ImmutableList.copyOf)。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
App如何实现就近接入?如何改善调度不准问题?
购买阿里云ECS服务器实例规格型号、功能、型号级别介绍及选择
232 0
一次有意思的错选执行计划问题定位(涉及SYS_OP_C2)
这两天和广分的兄弟看了一个问题,比较有意思,过程也比较曲折。。。 问题现象: 1. 11g的库,话说有一个应用程序新上线,应用中使用了绑定变量的方式执行一条简单的SQL,例如select a from b where c = :x,c列是该表复合主键的前导列,表定义是varchar2类型,从spotlight监控看这条SQL的执行计划是全表扫描,一次执行要1个小时,这张表是运行很久的引用分区表,数据量是亿级,测试的时候正常,但很显然测试的数据量可能和生产非常不一致,导致没察觉。
948 0
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
4479 0
记录一次 CLOSE_WAIT 问题排查和梳理
本文记录了一次排查CLOSE_WAIT过多问题的经历和事后梳理学习的过程
5778 0
Dev 日志 | 一次 Segmentation Fault 和 GCC Illegal Instruction 编译问题排查
本文记录了 Segmentation fault (core dumped) 和 internal compiler error: Illegal instruction 两个错误信息的 Debug 过程
1343 0
+关注
北征
专注Java领域互联网服务端技术
1
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
文娱运维技术
立即下载
《SaaS模式云原生数据仓库应用场景实践》
立即下载
《看见新力量:二》电子书
立即下载