Não falei que um messenger não tem que ter escalabilidade. Mas estou perguntando o quão escalavel você quer esse messenger.
Para milhares de contatos falando ao mesmo tempo, só o fato de ter várias Threads rodando já vai matar a aplicação. Controlar isso, somente saindo do Socket e indo para o SocketChannel e usando os Selectors para reduzir o número de Threads.
Depois, estou falando para manter um mapa que identifique a quem pertence cada conexão aberta. Afinal, quando a aplicação identificar que a mensagem é para esse alguém, basta consultar o mapa e encaminhar para a conexão correta. Como mapear para Strings resolve esse problema?
Gerenciar se a conexão está ou não ociosa é um problema que deve ser resolvido por outro trecho da aplicação. Se a aplicação decidir que, por qualquer razão que seja, a conexão deve ser fechada, ela terá que ser retirada desse mapa.
Não entendi o que você dizer com “é muito mais custoso ter uma conexão socket num mapa do que uma String com o endereço do cliente”. O mapa relacionada objetos entre si e, para isso, o java acaba usando referências (ponteiros, de poucos bytes). Do ponto de vista do mapa, não interessa se no mapa está uma String, uma conexão Socket ou uma imagem de 100mb.
Conforme eu disse, o mapa vai atuar sobre as conexões que já existem (afinal é para isso que a aplicação serve), portanto, criar as tais Strings é que seriam um custo a mais.