Leader:這樣的 Bug 你也寫的出來???

Hello~各位讀者新年好!不知道大家春節假期是否已延長,小黑哥剛接到通知,假期延長到 2 月 2 號,另外回去之後需要在家辦公,自行隔離兩週。還沒試過在家辦公,小黑哥就怕到時候生物鐘還沒調整過來,一覺睡醒已經是下午了。。。

前言

春節假期,還躺在牀上小黑哥,收到對賬系統的一條預警短信,提示當前系統資金覈對存在問題。關於資金的問題,都是大問題,小黑哥連忙拔出電腦,連上 VPN,登錄生產環境的查看相關日誌。

通過日誌,很快小黑哥定位到相關代碼。

有的同學可能一下子就能看出這裏的問題, Long 對象採用 !=進行比較,這真是一個低級 Bug。幸好 Leader 還不知道,趕緊悄悄修復一下。

現在回想小黑哥當初寫這段代碼的時候,誤以爲兩個 Long 對象比較將會進行自動拆箱,轉變爲兩個基本數值類型比較。

下面開始複習一下 Java 自動裝箱與拆箱機制。

自動裝箱與拆箱機制

自動裝箱(Autoboxing),是 JDK5 新增的一種語法糖,將會在代碼編譯時自動將原始類型轉換爲其對應的對象包裝器類。例如將 int 轉換爲 Integerdouble 轉換爲 Double。如果轉換結果相反,我們就將其稱爲拆箱。

下面是一個自動裝箱的例子:

上面代碼 li.add(i) 就發生自動裝箱,將基本數據類型 long 轉換爲其包裝類 Long

查看這段代碼對應的字節碼。

圖上黃線標註的字節碼對應的代碼爲 li.add(i)。從這我們可以看到 long 類型的自動裝箱實際上調用 Long#valueOf 方法。所以編譯器運行時將之前的代碼轉換爲下面的代碼

接下來我們來看一個自動拆箱的例子:

由於 Long 包裝類對象不能用於求模(%)以及 =,所以這段代碼將會發生自動拆箱,將 Long 轉化爲 long 類型。相應的這裏我們也看下其編譯之後的字節碼。

這裏的字節碼比之前的複雜很多,這裏主要關注黃線部分。可以看到這裏調用 Long#longValueLong 對象轉爲 long 類型。所以自動拆箱這個例子,最後編譯器生成字節碼等同於以下代碼:

Java 規定的 8 種數據類型都有其對應包裝類,這些都可以進行相應的自動裝箱和拆箱。

這裏小結一下:

  1. 自動裝箱機制是通過調用包裝器類 valueOf 實現
  2. 自動拆箱機制通過調用包裝器類的相應的 **Value ,如 longValue, intValue 實現

Cache 陷阱

自動裝箱和拆箱概念說起其實挺簡單的,但是如果使用不當可能就會踩坑。

我們來看一段代碼:

如果你對上面的結果的不是很清楚,恭喜你,暖男小黑哥幫你排雷了。

上面輸出結果爲:

true
false

這裏輸出結果之所以爲這樣,主要與 LongCache 有關。自動裝箱進制將會調用 Long#valueOf,其源碼如下:

只要數值範圍位於 [-128,127] 之間,valueOf 就會返回 LongCache.cache 這個數組中的值。所以 aLong/bLong其實是同一個對象,cLong/dLong 是兩個不用對象的。

Long#valueOf 這個方法通過 Cache 減少創建對象的數量,提高相應的空間以及時間性能。

至於爲什麼緩存 [-128,127] 之間的數字,而沒有緩存更多的值,甚至緩存所有的值?

這是因爲 Long 範圍爲**[-263,263]**,總共 2^64 ,這麼多對象實例,顯然不宜全部緩存。至於 [-128,127] 之間數字 JDK 設計者認爲這些數字使用頻率高,個人認爲這是一個經驗值。

另外這個 cache可能會導致另外一個問題:鎖共用。

上面代碼看起來 A 類使用 along 這個對象鎖,而 B 類使用 blong 這個對象鎖,看起來兩個風馬牛不相及,但是實際上兩個對象由於 Cache 機制,導致其實際上使用同一個對象,AB 共用同一把鎖。

除了 Long#valueOf 方法中存在 Cache 以外,ByteShortIntegerCharacter 也存在相應的 Cache 值。其中 Integer 對象可以通過 -XX:AutoBoxCacheMax=<size> 改變 Cache 初始範圍。

技術總結

通過自動裝箱與拆箱機制,大大減少了數據類型轉化的代碼,但是同時帶來一些隱藏的問題。這裏我們需要記住:

  1. 所有對象之間不能使用 == 比較,需要使用 equals 代替,推薦使用 JDK7 提供的 Objects#equals 方法,該方法可以有效避免比較過程空指針問題
  2. 基本數據類型的包裝類不適合當做鎖對象

各位讀者朋友們,請牢記這兩個總結,不要踩中這兩個坑哦。另外推薦大家安裝一下 FindBugs 這個插件,通過這個插件掃描代碼,可以找出代碼中的相關缺陷。上面兩個問題,通過 FindBugs都能掃描出來,簡直神器。

對象比較問題:

鎖問題

幫助文檔

  1. Autoboxing and Unboxing『https://docs.oracle.com/javase/tutorial/java/data/autoboxing.html』
  2. 極客時間-『Java併發編程實戰』專欄

歡迎關注我的公衆號:程序通事,獲得日常乾貨推送。如果您對我的專題內容感興趣,也可以關注我的博客:studyidea.cn

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章