問題
再設定java記憶體堆棧為512m的時候.定時任務一天就會崩潰,各種連結斷開 (第一反應,以為伺服器斷網了)
第二次将java記憶體堆棧設定為4g.定時任務間隔了7天成功再次崩潰,連結斷開,并且.捕捉到java堆棧溢出的錯誤
重新開機後.開始進行每天1次的記憶體堆棧跟蹤
具體指令如下
檢視目前已啟動的程序和對應的PID
jps
将目前記憶體映射出1個檔案
jmap -dump:format=b,file=heap.bin <pid>
從docker檔案種拷貝出記憶體分析檔案
docker cp <id>:/heap.bin
通過jumpserver将此檔案拉取到本地
Java自帶記憶體分析程式
jhat -J-Xmx512m heap.bin
很遺憾 3.3g的記憶體檔案顯而易見的記憶體溢出不能分析, 請原諒公司給我配的電腦隻有8g記憶體
于是使用JProfiler 進行記憶體分析
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL90zZihmUYl1b502YxQnMMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZwpmL4IjN4UDO1ETMxIDOwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
這邊可以觀察到占用記憶體最大的是一個char[]類型的數組
進入内部分析,觀察到對象是由druid持有的
于是進入druid檢視源碼
private final void internalAfterStatementExecute(StatementProxy statement, boolean firstResult, int... updateCountArray) { final long nowNano = System.nanoTime(); final long nanos = nowNano - statement.getLastExecuteStartNano(); JdbcDataSourceStat dataSourceStat = statement.getConnectionProxy().getDirectDataSource().getDataSourceStat(); dataSourceStat.getStatementStat().afterExecute(nanos); final JdbcSqlStat sqlStat = statement.getSqlStat(); if (sqlStat != null) { sqlStat.incrementExecuteSuccessCount(); sqlStat.decrementRunningCount(); sqlStat.addExecuteTime(statement.getLastExecuteType(), firstResult, nanos); statement.setLastExecuteTimeNano(nanos); if ((!firstResult) && statement.getLastExecuteType() == StatementExecuteType.Execute) { try { int updateCount = statement.getUpdateCount(); sqlStat.addUpdateCount(updateCount); } catch (SQLException e) { LOG.error("getUpdateCount error", e); } } else { for (int updateCount : updateCountArray) { sqlStat.addUpdateCount(updateCount); sqlStat.addFetchRowCount(0); StatFilterContext.getInstance().addUpdateCount(updateCount); } } long millis = nanos / (1000 * 1000); if (millis >= slowSqlMillis) { // slowSqlMillis= 3000 // 解析查詢條件 String slowParameters = buildSlowParameters(statement); // 大對象LastSlowParameter的由來 sqlStat.setLastSlowParameters(slowParameters); String lastExecSql = statement.getLastExecuteSql(); if (logSlowSql) { LOG.error("slow sql " + millis + " millis. " + lastExecSql + "" + slowParameters); } handleSlowSql(statement); } } String sql = statement.getLastExecuteSql(); StatFilterContext.getInstance().executeAfter(sql, nanos, null); Profiler.release(nanos); }
public JdbcSqlStat createSqlStat(String sql) { lock.writeLock().lock(); try { JdbcSqlStat sqlStat = sqlStatMap.get(sql); if (sqlStat == null) { sqlStat = new JdbcSqlStat(sql); sqlStat.setDbType(this.dbType); sqlStat.setName(this.name); sqlStatMap.put(sql, sqlStat); } return sqlStat; } finally { lock.writeLock().unlock(); } }
可以看到.他是通過sql直接進行映射的.是以.當使用insert into values 的時候.由于每次定時任務插入的資料都不同,導緻sqlStatMap存入的sql語句越來越多.最好導緻記憶體溢出,将大部分的連結都斷掉
修改方法
1.使用insert into values 的控制每次values的長度,控制到一緻
2.修改durid的源碼,使其抛出insert的文法
3.更換HikariCP資料源