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

引子

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

现象

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

// 对象结构体
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)。

时间: 2024-09-17 04:10:57

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

记一次 Node.js 内存泄漏排查

首先感谢 alinode 团队 https://alinode.aliyun.com 的支持. 大概一个月前,在 alinode 管理页面看到内存占用成锯齿状上升,虽然上涨速度很慢,但是最低点与最高点都在稳定上涨,意识到应该是内存泄漏了. 虽然有关内存泄漏方面的文章读了一些,也知道需要看内存快照,内存时间线等日志来分析内存泄漏,但是真正自己上手时还是有些懵逼了.使用 alinode 将程序运行不同时间点的内存快照导出然后对比分析(这时使用的是 devtools),因为对于 devtools 了解

[置顶]【结果很简单,过程很艰辛】记阿里云Ons消息队列服务填坑过程

    Maybe 这个问题很简单,因为解决方法是非常简单,但填坑过程会把人逼疯,在阿里云ONS工作人员.同事和朋友的协助下,经过一天的调试和瞎捣鼓,终于解决了这个坑,把问题记下来,也许更多人在碰到类似问题的时候,会开放思路.当然不得不说,Ons的.NET接口还很不完善,甚至没有独立在Windos 2008/2012服务器测试过,希望官方加把力. 1.阿里云ONS介绍    ONS(Open Notification Service)即开放消息服务,是基于阿里开源消息中间件MetaQ(Rocke

php学习随记4

php 操作数组 (合并,拆分,追加,查找,删除等) 博客分类: Php / Pear / Mysql / Node.js 1. 合并数组 array_merge()函数将数组合并到一起,返回一个联合的数组.所得到的数组以第一个输入数组参数开始,按后面数组参数出现的顺序依次迫加.其形式为: array array_merge (array array1 array2-,arrayN) 这个函数将一个或多个数组的单元合并起来,一个数组中的值附加在前一个数组的后面.返回作为结果的数组. 如果输入的数

记一次失败的jQuery优化尝试

我经常抱怨jQuery的DOM操作性能并不优秀,并且经常尝试用一些方法去进行优化,但是越是优化,越是沮丧地发现jQuery其实已经做得很好,从使用者的角度能够进行的优化实在有限(这并不意味着jQuery的性能是优秀的, 反之只能说它是一个相对封闭的库,无法从外部介入进行优化).这篇文章就记录一次失败的优化经历. 优化思想 这一次优化的思想来自于数据库.在数据库优化的时候,我们常会说将大量的操作放在一个事务中一起提交,能有效提高效率.虽然对数据库不了解的我并不知道其原因,但是事务的思想却为我指明了

php的双向队列类

(deque,全名double-ended queue)是一种具有队列和栈的性质的数据结构.双向队列中的元素可以从两端弹出,其限定插入和删除操作在表的两端进行. 在实际使用中,还可以有输出受限的双向队列(即一个端点允许插入和删除,另一个端点只允许插入的双向队列)和输入受限的双向队列(即一个端点允许插入和删除,另一个端点只允许删除的双向队列).而如果限定双向队列从某个端点插入的元素只能从该端点删除,则该双向队列就蜕变为两个栈底相邻的栈了. DEQue.class.php <?php /** php

php实现的双向队列类实例

  本文实例讲述了php实现的双向队列类及其用法,对于PHP数据结构与算法的学习有不错的参考价值.分享给大家供大家参考.具体分析如下: (deque,全名double-ended queue)是一种具有队列和栈的性质的数据结构.双向队列中的元素可以从两端弹出,其限定插入和删除操作在表的两端进行. 在实际使用中,还可以有输出受限的双向队列(即一个端点允许插入和删除,另一个端点只允许插入的双向队列)和输入受限的双向队列(即一个端点允许插入和删除,另一个端点只允许删除的双向队列).而如果限定双向队列从

深入Guava源码之Stripe

当前JDK对并发编程的支持 Sun在Java5中引入了concurrent包,它对Java的并发编程提供了强大的支持.首先,它提供了Lock接口,可用了更细粒度的控制锁的区域,它的实现类有ReentrantLock,ReadLock,WriteLock,其中ReadLock和WriteLock共同用于实现ReetrantReadWriteLock(它继承自ReadWriteLock,但是没有实现Lock接口,ReadWriteLock接口也没有继承Lock接口).而且,它还提供了一些常用并发场景

Linux TC 带宽管理队列规则

      在着手学习TC之前,请先了解TC采用如下单位来描述带宽:       mbps = 1024 kbps = 1024 * 1024 bps => byte/s       mbit = 1024 kbit => kilo bit/s       mb = 1024 kb = 1024 * 1024 b => byte       mbit = 1024 kbit => kilo bit       内定:数值以bps和b方式储存.但当设置tc输出速率时,使用如下表示:

分布式环境下限流方案的实现redis RateLimiter Guava,Token Bucket, Leaky Bucket

业务背景介绍 对于web应用的限流,光看标题,似乎过于抽象,难以理解,那我们还是以具体的某一个应用场景来引入这个话题吧. 在日常生活中,我们肯定收到过不少不少这样的短信,"双11约吗?,千款-.","您有幸获得唱读卡,赶快戳链接-".这种类型的短信是属于推广性质的短信.为什么我要说这个呢?听我慢慢道来. 一般而言,对于推广营销类短信,它们针对某一群体(譬如注册会员)进行定点推送,有时这个群体的成员量比较大,譬如京东的会员,可以达到千万级别.因此相应的,发送推广短信的