Então, o que está acontecendo é que uma de suas JVMs do Tomcat está tentando exceder o heap de 2048 MB que você alocou para ela.
Parece que você está procurando uma resposta específica, tanto quanto uma lista de verificação de coisas para tentar, então aqui vai:
Ficar sem heap é devido a um vazamento de memória, em que você vaza um pouco com cada solicitação ou, em um cenário de carga, pode ser porque você está apenas jogando mais na JVM do que pode manipular. Você quer identificar qual deles é o problema. Comece analisando como você gera sua carga.
Se o problema ocorrer apenas em um alto nível de solicitações paralelas, mas nunca em um nível inferior, você terá um problema de que o número de solicitações vezes a memória necessária para processar cada solicitação seja muito grande. Você precisa fazer com que cada solicitação use menos memória ou limitar a simultaneidade de alguma forma.
Se o problema ocorrer após um determinado número de solicitações terem sido processadas, independentemente da simultaneidade, você terá um vazamento de memória. Você precisa encontrá-lo e recuperar a memória.
Em qualquer cenário, você será muito auxiliado por ter um bom analisador de memória de heap. Existem bons comerciais como o YourKit Java Profiler, ou gratuitos como o Eclipse Memory Analyzer. Encontre uma ferramenta que funcione para você e aprenda a usá-la para ver o que está ocupando a memória. Observe que você não precisa necessariamente usar a ferramenta para iniciar seu programa - se você estiver executando um teste de carga em um servidor, poderá usar a ferramenta de linha de comando jmap do JDK para capturar um dump de heap em um arquivo, e, em seguida, use sua ferramenta para analisar o arquivo de despejo. A ferramenta mostrará a você quais objetos estão ocupando espaço no seu heap.