tl;dr; try starting Erlang with '
+MEas bf'
Long explanation:
If the memory is used by the ets_alloc allocator, it can be either actively used by ETS tables (such as Mnesia disc_copies tables) or it can be lost due to memory fragmentation. I guess this is memory fragmentation in your case, but let's quickly rule out the other possibility first. (By the way, in either case, garbage collecting processes won't reclaim the memory. GC operates on eheap_alloc allocated memory.)
So if erlang:memory(ets) is close to what recon reports as allocated, the memory is in use. Your system wrote a lot of data to ETS/Mnesia, and didn't remove it afterwards. Inspect the contents of the tables and figure out what was left there that shouldn't be there, which part of the application should have removed that data and why didn't it do its job properly.
The other, more likely option is fragmentation. I also experienced that the default memory allocation strategy (aoffcbf = address order first fit carrier best fit) can perform poorly when you use a lot of memory. All address order first fit strategies will use heavily the multiblock carriers with the lowest memory addresses, and if you have many carriers, those placed higher in memory will have less and less chance to be used. In my particular case ets_alloc created a total of 150k multiblock carriers for storing ~1.2TB data in ETS tables. This resulted in ~100 GB unused memory being wasted in the high address carriers. You have a much smaller system, but depending on the usage patterns of your ETS data you can end up in a similar situation.
You can check the number of carriers with erlang:system_info({allocator, ets_alloc}). It will print something like this:
[{instance,0,
[{versions,"0.9","3.0"},
{options,[...]},
{mbcs,[...
{carriers,1,1,1}, %% <- number of multi block carriers = 1
...]},
{sbcs,[...
{carriers,0,0,0}, %% <- number of single block carriers = 0
...]},
{calls,[...]}]},
{instance,1,...
Check theaw numbera across all your allocator instances. You will typically have very few single block carriers (unless you store huge records in ETS). In my experience, fragmentation correlates well with the number of carriers an allocator handles, and can become quite significant above ~10 carriers.
So, If you have the same problem I described, I have some bad news and some good news. The bad news is that I don't know a way of forcing the VM to defragment memory and get rid of the waste. The good news is that the bf (best fit) allocation strategy (which used to be the default up to R16) performs much better when you have many carriers. You need to pass the '+MEas bf' command line argument to the VM to switch ets_alloc to bf strategy.
Hope it helps,
Daniel