問題與分析
今天發現服務器上的Jenkins在集成項目時報錯,報錯原因如下:
error compiling: java.lang.StackOverflowError -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
還是頭回遇到這種錯誤,由於Jenkins每天晚上都會自動集成當天提交的代碼。之前一直編譯正常,而今天編譯報錯,正好昨天我提交了代碼上去。
很顯然,這是由於我提交的代碼造成的。一開始以為是項目代碼有問題,比如無限遞歸之類的導致的堆棧溢出。但是在本地發現可以正常運行該Maven項目,無論是編譯還是打包都一切正常。難道是Jenkins抽風了?於是重啟了Jenkins任務去重新編譯一次項目,結果再次掛了,報了同樣的錯誤。
百度之后發現挺多人都遇到這種問題,原因也基本一樣,都是因為jvm內存不足導致的堆棧溢出。仔細想想,確實很有可能,因為我在本地是通過eclipse來運行項目的,我在本地配置了足夠的jvm參數;而服務器上則可能由於Jenkins同時運行多個任務,又或者配置的內存不夠,所以就編譯報錯了。
再一次審視我昨晚提交的代碼,發現在一個測試類里,有個方法調用了非常多的api,這個方法的方法體里大概調用了四百多個api。之所以會方法體這么長,是因為該方法用來測試某個pojo類里的字段是否和數據庫里的字段能否匹配上。因為pojo里的字段有四百多個,所以就導致該方法體如此之冗長了。
也就是說,很有可能是該方法導致Jenkins在運行該測試類的該方法時調用api過多導致堆棧溢出了。於是重新修改了代碼,將該方法里調用的api分別放到了另外兩個方法里,大概一個方法里調用兩百個api,然后測試類原來的方法則調用這兩個新添加的方法。
之后提交代碼,重新讓Jenkins集成代碼,發現不再報錯。
解決方法
和老大討論之后,決定采用修改代碼的方式去fix這個問題。可能是處於服務器內存緊張的考慮,又或者是別的什么因素,不采用網友博文里說的加大jvm參數的方案。
這里還是總結下這兩個方案:
方案一
在項目的啟動腳本里加大jvm參數,如下:
set MAVEN_OPTS=-Xss4096k
也可以不用設置得那么大,比如設置成下面這樣:
set MAVEN_OPTS=-Xss2m
方案二
修改代碼內某個調用了過多api的方法,可以將其一分為二,拆分成多個子方法,各自調用一部分的api,最后由原來的方法來call這幾個拆分開來的子方法。總之,應當避免在一個方法內調用過多api。
其他
這里順便貼一下公司項目設置的參數:
echo off
setlocal
set MAVEN_DEBUG_OPTS=-Duser.timezone=GMT+8 -Xdebug -Xmx4096M -XX:PermSize=128M -XX:MaxPermSize=512M -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=8088,server=y,suspend=n
mvn spring-boot:run
endlocal