Duffymo está quase certo. No passado, quando tínhamos vazamentos de memória, era praticamente SEMPRE o driver MySQL JDBC. Apenas esquecendo de fechar um pequeno ResultSet ou Connection ou Statement em algum lugar. Acabei auditando toda a base de código toda vez que os usamos para encontrar o problema e garantir que eles fossem fechados.
Quanto ao HashMap, também já vi isso. Eu não olhei para a fonte, mas minha impressão foi que o driver MySQL armazenava as linhas (pelo menos valores de linha) em HashMaps internamente.
O vazamento de ResultSets é tristemente fácil. A ideia desses recursos que podem ser fechados que cuidam disso eles mesmos chegando no JDK 7 ou 8 realmente me atrai por esse motivo.
Você pode inserir uma classe shim em algum lugar (digamos para Connection) para registrar cada recurso aberto/fechado para ver se você pode detectar onde está o vazamento sem ler diretamente toda a sua fonte.