今日排查一個遞歸導致的StackOverflow 問題時, 嘗試用jconsole.exe連到 IDEA 中跑的java程序所在的進程ID時, 一直報連接錯誤, 如下:
重試幾次都沒什麼效果, 隨查看防火牆的設置情況, 發現防火牆已關閉. 排除這方面的問題.
如果是Linux, 可以考慮設置防護牆通過策略:
①. 打開防火牆文件: vi /etc/sysconfig/iptables
②. 添加-A INPUT -m state --state NEW -m tcp -p tcp --dport 8022 -j ACCEPT, 需與(Dcom.sun.management.jmxremote.rmi.port一致)
③. 重啓防火牆, 使其生效 :service iptables restart
後經調試發現, 未合理的配置JXM參數.
JMX(JAVA 管理拓展) 配合Jconsole 來使用是用來分析JVM狀態, 進而發現進程潛在問題的必要途徑, 故合理配置JMX參數至關重要。
遂嘗試使用如下jmx配置參數進行調試, 可以通過測試.
核心配置參數如下:
# 設置jmx遠程配置
-Dcom.sun.management.jmxremote
# 設置jmx遠程配置的port
-Dcom.sun.management.jmxremote.rmi.port=8022
# 設置jmx遠程配置的ssl
-Dcom.sun.management.jmxremote.ssl=false
# 設置jmx遠程配置的鑑權
-Dcom.sun.management.jmxremote.authenticate=false
# 設置jmx遠程配置的host地址
-Djava.rmi.server.hostname=localhost
IDEA中的配置樣例如下:
引申:
java.lang.StackOverflowError: null
異常的一般的原因是:
程序產生了死循環
或 發生無限遞歸
.
StackOverflow問題起因:
程序在使用MyBatis-Plus時, 在處理一段插入或更新邏輯(insertOrUpdate)時, 自己準備覆蓋 MyBatis-Plus裏 BaseMapper 裏的 insertOrUpdate方法, 以實現自定義判斷更新的數據條目, 而非根據Entity中所有字段實現全量更新覆蓋. 但在方法命名時, 錯誤的命名爲了update方法, 導致後面在處理數據庫中沒有的業務條目時, 原意是想使用MP裏BaseMapper的insert方法, 但實際上是遞歸調用了該自定義方法.
這將導致所有的操作壓力, 全部指向如下數據庫操作語句selectList :
List existsList = selectList(condition);
/**
* 業務方法, 會調用如下insert(實際上應爲: insertOrUpdate)
* 來處理Entity 在數據庫中的 插入或更新問題.
*/
public void myBizImpl () {
insert(softwareDownload);
}
/**
* Biz Desc :
*
* 軟件下載情況統計數據到DB,
* 如果數據庫存在該聯合主鍵的數據, 即進行更新;
* 如果不存在, 即進行插入.
*
* @param softwareDownload Model層的軟件下載業務Entity
*/
public boolean insert(SoftwareDownload softwareDownload){
Wrapper condition = Condition.create().eq("DOWNLOAD_DATE", softwareDownload.getDownloadDate()).and().
eq("DOWNLOAD_CHANNEL", softwareDownload.getDownloadChannel());
List existsList = selectList(condition); // MP裏baseMapper裏的方法
if ( null != existsList && 0 < existsList.size() ) {
return update(softwareDownload, condition);
} else {
return insert(softwareDownload);
}
}
解決方案:
將上述方法名 insert 改爲 insertOrUpdate, 目的是覆蓋BaseMapper的insertOrUpdate方法, 用以實現自定義更新.
修改後的方法頭爲 :
public boolean insertOrUpdate(SoftwareDownload softwareDownload){
}
其實上述實現數據庫insertOrupdate方法 尚存在一些性能問題, 每次操作都會進行一次query檢查操作, 更爲優雅的詳見引用列表[^1].
通過查詢數據庫的壓力時, 可以順便跟蹤一下數據庫連接池的底層參數配置, 比如 maxActiveSize的默認配置(可以在application-druid.yml裏進行覆蓋).
這時, 查詢mysql連接進程數, 看到如下有8個連接.
show PROCESSLIST;
結合DruidAbstractDataSource源碼查看, 確實是8個:
引用列表:
[^1] MySql實現無則插入有則更新的解決方案