Since the entity framework keeps data in memory(as do many ORM's), then as with many in-memory collections there are probably internal arrays. As you add items to a collection, the internal array doubles in capacity.
For example, if you have a collection like an ArrayList containing 256 items, and add the 257th item to it, then what happens internally is a new block of memory is allocated for a 512 item array, they 256 item array is copied to the new 512 item array, and then the 256 item array is made available for garbage collection. Thus at the point of transition you will have 768 items allocated in memory just because you added the 257th item. I have run into headaches with this when using memorystream, because you need almost 3 times as much contigous unfragmented memory than what you really need. This is the .Capacity property you see on collections, and it is almost always a power of 2(since it double in size as needed).
My bet is there are internal arrays that double in size as needed to support your collections of in memory objects. So 300,000 objects of the same type would probably be held in an internal array of size 524,288. Additionally, if it's like similar techniques elsewhere in the .NEt Framework, then whenever the 262145th item was added, both an array of 262144 and 524288 existed in memory while the items were copied to the new array. A total of 786432 items in memory. The old array would stick around until the garbage collector decided it wasn't needed anymore.
There might be some options in the Entity framework regarding concurrency support that you could disable which might improve memory usage. I am only speculating here though, but to support concurrency they store in-memory both the current version of the data, and it's original version for comparison to support concurrency.
I would also look at filtering what data you are interacting with. Try to find clever criteria to limit what is queried and loaded in memory. For example, if you had an application that allowed a user to edit customer accounts, but only certain accounts were assigned to them, then use that as your filtering criteria so that you only load in memory those accounts that the user could potentially interact with.