问题
再设置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 进行内存分析
这边可以观察到占用内存最大的是一个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数据源