使用Mybatis導致的OutOfMemoryError錯誤

使用Mybatis導致的OutOfMemoryError錯誤

使用Mybatis導致的OutOfMemoryError錯誤

我們系統中是用Mybatis開發,這個系統在生產環境運行了好幾個月的時間了,沒有出現過很嚴重的問題,突然有一天集群中的有2臺報警無法提供服務,運維重啟服務後正常。因為是生產上的問題,我們需要定位根本原因,查了那兩臺機器的日誌,發現有OutOfMemoryError錯誤,錯誤信息如下

使用Mybatis導致的OutOfMemoryError錯誤

OutOfMemoryError

錯誤最終發生map的put上面,這個map是在UnknownTypeHandler的resolveTypeHandler方法中調用的。這個是現象,不是導致OutOfMemoryError的根本原因,但是這個方法確實會額外佔用一些內存。(根本原因是數據量突然增多,一次查出了上百萬的數據)

數據量大了這個方法也是挺佔用內存的,下面就先對這個方法分析:

這個方法首先獲取ResultSet的MataData,也就是每列的元數據,把這些信息組裝成一個map,通過傳入的列和這個map,確認當前列的位置。最後通過MataData和列的位置推斷出當前列的TypeHandler。如果查出的列很多,數據量很大的情況下這個臨時的map就有可能是壓死駱駝的最後一根稻草。

使用Mybatis導致的OutOfMemoryError錯誤

resolveTypeHandler

Mybatis的TypeHandler

說到UnknownTypeHandler,就先看看TypeHandler,mybaits在查詢出來的resultSet的每一列都會通過TypeHandler轉成Java需要的類型,這個就是類型轉換。Mybatis內置了很多的TypeHandler,如果框架提供的不能滿足需求,我們也可以自定義一個TypeHandler。如果部署下TypeHandler,請參考下文:

Mybatis之TypeHandler簡析

為什麼會用UnknownTypeHandler呢?

如果代碼走到了UnknownTypeHandler中,說明在程序啟動解析resultmap的時候沒有找到具體的TypeHanlder,只能藉助運行期間推斷出具體的TypeHanlder,這樣程序就會多執行很多代碼,影響了效率,也可能導致OOM的發生。

UnknownTypeHandler的註冊

初始化的時候,會在TypeHandlerRegistry中註冊所有的TypeHandler也包括UnknownTypeHandler,註冊了三種情況下使用UnknownTypeHandler。

  1. column的JavaType是Object
  2. column的JavaType是Object,JdbcType是OTHRE
  3. column的JdbcType是OTHRE
使用Mybatis導致的OutOfMemoryError錯誤

UnknownTypeHandler

除了以上,如果我們resultMap的type為java.util.Map,並且JavaType和typeHandler都沒有定義,此時框架推斷不出來具體的typeHandler,最終也會設置為UnknownTypeHandler。

如果resultMap的type為實體類,並且JavaType和typeHandler都沒有定義,如果設置了當前類的property屬性,框架推斷到property指定變量的set方法的類型。

下面是我測試用的一個resultmap,所有列的類型都會用UnknownTypeHandler解析。

使用Mybatis導致的OutOfMemoryError錯誤

resultmap

說了這麼多,總結一點:寫代碼的時候能明確就明確,少讓框架自動去推斷,推斷是有成本的代價的。


分享到:


相關文章: