日志導致jvm內存溢出相關問題


  1. 生產環境日志級別為info,請看如下這行代碼:
LOGGER.debug("the DTO info: {}", JSON.toJSONString(DTO));

這段代碼主要有兩個需要注意的地方:

  • 日志級別為info,而線上環境是warn級別。我們可以得出結論,線上環境肯定不會輸出這行日志。
  • 打印日志的行為中有JSON序列化動作。
    第二點是此文的關鍵。我們假設DTO是一個很小的對象,JSON序列化時間以及開銷可以忽略不計,那么這行代碼依然沒有問題。但是,如果DTO是一個很大的對象,比如10k,甚至100k,即使快如fastjson,其耗時依然高達數百毫秒,並且非常消耗CPU。如果是在高並發的系統中,這么大的開銷完全不可接受,甚至可能就會拖垮整個系統。

有同學就會說了,我不是info日志么,為什么還會執行這行代碼?請繼續往下看。我們首先看一下slf4j中logger.info()這個方法是如何申明的:第二個參數為Object類型。我們的代碼中傳遞給第二個參數的值為:JSON.toJSONString(DTO),很明顯這行代碼是傳遞一個String類型的字段給Object arg。那么String如何來呢?答案也很明顯,必須先執行JSON序列化才能得到String,所以不論日志是否打印,JSON序列化都會執行。那么logger.info這個info在什么時候起作用呢?答案是它只能 在輸出日志這個動作時起作用:
2. 解決方案
如何解決這個問題?很簡單,在輸入日志時加個級別判斷(需要說明的是,這種規范很容易被忽略,比如項目成員更替時,很容易引入有問題的代碼。所以筆者寫了一段腳本:掃描所有Java代碼,如果logger.info()中有JSON序列化動作,那么必須判斷優先級后才能輸出日志。即可以簡單的認為它的前一行代碼必須是logger.isInfoEnabled()。如果你的項目有CICD環境,那么把這段腳本集成到掃描規范中,才是解決這個問題最完美的方案):

if (LOGGER.isInfoEnabled()) {
    LOGGER.info("the DTO info: {}", JSON.toJSONString(DTO));
}

https://mp.weixin.qq.com/s/A_5llN3OiY5dm7T8yOOimQ


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM