Easy (but lazy) solution to most of those allocations would be to use LazyCacheStrings and change your code into something like this:
public static LazyCacheStrings<ItemType> ItemTypeStrings = new LazyCacheStrings<ItemType>( (t)=>t.ToString() );
static LazyCacheStrings<System.ValueTuple<float,float>> TitleStrings = new LazyCacheStrings<System.ValueTuple<float,float>>( (kv)=>string.Format("Items {0:0} / {1:0}",kv.Item1,kv.Item2) );
static LazyCacheStrings<System.ValueTuple<ItemType,float>> SectionStrings = new LazyCacheStrings<System.ValueTuple<ItemType,float>>( (kv)=>string.Format("{0} {1:##}",ItemTypeStrings[kv.Item1],kv.Item2) );
void Update ()
{
ItemContainer container = SingletonMB<StationContent>.Instance.GetContainer();
title.text = TitleStrings[ ( container.CapacityUsed , container.Capacity ) ];
sectionLines.RemoveAllLines();
int numContainerItems = container.Items.Count;
for( int i=0 ; i<numContainerItems ; i++ )
{
ItemStack itemStack = container.Items[i];
sectionLines.Set(
ItemTypeStrings[ itemStack.Type ] ,
SectionStrings[ ( itemStack.Type , itemStack.Amount ) ]
);
}
}
This should limit worst of this runaway allocations just some seconds after game start (once caches fill up). It's not the best solution but certainly the simplest one to implement.