測不準原理?記一次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)。
相關文章
- 記一次oom問題排查OOM
- 記錄一次問題排查
- 記一次SparkStreaming不產生新的batchJob的問題排查SparkBAT
- 記一次排查CPU高的問題
- 記一次 Laravel MethodNotAllowedHttpException 問題排查LaravelHTTPException
- 記一次線上FGC問題排查GC
- 記一次OOM問題排查過程OOM
- 【問題排查篇】一次業務問題對 ES 的 cardinality 原理探究
- 從一次問題排查聊聊問什麼要懂原理
- 記一次線上websocket返回400問題排查Web
- 記一次 MySQL 資料庫問題排查MySql資料庫
- 記一次線上崩潰問題的排查過程
- 記一次棧溢位異常問題的排查
- Laravel佇列相關問題記錄Laravel佇列
- 分析 Laravel 佇列實現原理解決問題Laravel佇列
- 記一次 Kafka 重啟失敗問題排查Kafka
- 記一次線上報錯日誌問題排查
- 一次容器MySQL的效能問題排查MySql
- 記一次協助排查許可權問題的經歷
- SQL調優日記:並行等待的原理和問題排查SQL並行
- 線上問題排查:記一次 Redis Cluster Pipeline 導致的死鎖問題Redis
- 一次快取效能問題排查快取
- 關於PHP佇列的問題PHP佇列
- 記一次安卓手機水印顯示問題的排查歷程安卓
- 【問題排查篇】一次業務問題對 ES 的 cardinality 原理探究 | 京東雲技術團隊
- 深入剖析:如何使用Pulsar和Arthas高效排查訊息佇列延遲問題佇列
- 排查Java的記憶體問題Java記憶體
- Laravel-5.8 佇列的清除問題Laravel佇列
- 一次線上問題的排查解決過程
- 一次線上CPU高的問題排查實踐
- 一次線上問題排查所引發的思考
- JAVA死鎖排查-效能測試問題排查思路Java
- 【問題排查系列】JDK1.8 下記憶體不斷增長排查及解決JDK記憶體
- 記一次記憶體溢位問題的排查、分析過程及解決思路記憶體溢位
- 記錄一次詭異的拼接sql不生效問題SQL
- 記一次hadoop yarn環境無法提交任務的問題排查HadoopYarn
- 一次ygc越來越慢的問題排查過程GC
- 《不測的祕密:精準測試之路》筆記筆記