timer每隔1天时间就会outofMemory一次,,,,,很恼火,,,,
思路:
第一步:必须拿到heapdump文件,,,
在jvm启动参数加上 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=日志路径,这样在下次outofmemory的时候 可以生成heapdump文件
第二步: 分析heapdump文件
发现ibatis缓存有大量数据,,,,,,可以从缓存里面看到是哪一条sql的数据
从ibatis缓存的key可以了解到是哪个sql导致的,,,,,,
但是问题来了,,,,为什么ibatis有这么多缓存没有释放,这些缓存缓存的内容都是同一条查询,只是limit不同而已。
所以需要通过源码找到ibatis缓存释放的代码,,,,,,
发现是在SqlSessionTemplate 里面(公司的ibatis是跟spring集成的)
sqlSession的操作其实是通过代理实现的,,,,
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) {
notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
notNull(executorType, "Property 'executorType' is required");
this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
this.sqlSessionProxy = (SqlSession) newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class },
new SqlSessionInterceptor());
}
看看代理的代码
private class SqlSessionInterceptor implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
final SqlSession sqlSession = getSqlSession(
SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType,
SqlSessionTemplate.this.exceptionTranslator);
try {
Object result = method.invoke(sqlSession, args);
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
// force commit even on non-dirty sessions because some databases require
// a commit/rollback before calling close()
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) {
notNull(session, "No SqlSession specified");
notNull(sessionFactory, "No SqlSessionFactory specified");
SqlSessionHolder holder = (SqlSessionHolder) getResource(sessionFactory);
if ((holder != null) && (holder.getSqlSession() == session)) {//如果使用spring事务,,,会在事务结束后统一释放缓存内存。如果事务没有结束缓存是不会释放的
if (logger.isDebugEnabled()) {
logger.debug("Releasing transactional SqlSession [" + session + "]");
}
holder.released();
} else {//如果没有使用spring事务,,,每一次sqlSession操作结束后 立马释放缓存
if (logger.isDebugEnabled()) {
logger.debug("Closing non transactional SqlSession [" + session + "]");
}
session.close();
}
}
通过以上分析,,,问题大概找到了,,,某个地方用了spring 事务,,该事务里面执行大量的读操作,,,读太多 直接把内存用完了,,
参考
关于 Shallow Size 跟 Retained Size
http://kenwublog.com/understand-shallow-and-retained-size-in-hprofling
http://blog.csdn.net/xch_w/article/details/7857047