This page describes tips and tricks for operating Elasticsearch clusters.
Elasticsearch's built-in balancing algorithm considers #of shards/node and #of shards in a given index/node. See: https://github.com/simplymeasured/tempest.
G1GC garbage collector works well.
Picking the right combination of CPU + memory + disk is crucial for an efficient Elasticsearch cluster. Most likely disk performance and memory are the biggest limiters.
Always stick to ~30-32G memory max - this is a Java limitation, anything over this value (determined by each install) will halve the actual memory that can be used due to pointer compression. See the Elastic documentation.