Memory utilization by SQL Server
First, you have a scan count of 17. I.e., it re-reads the same set of data over and over again, and the total for those are 4616999 logical reads. Assuming that the exact same set of data is re-read 17 times, you would divide your 30 GB with 17 to get the amount of data read from disk. So, at first pass it reads som 1.8 GB data, and then it re-reads this same set of data 16 times more. 1.8 GB data is no problem to fit in memory.
Second, even if you did have a scan count of 1, and the data wouldn't fit in memory, then where would the problem be? As it read some data, that data can later removed from memory during the read as it get further along the read operation.
Finally, don't get fooled by "physical reads" from statistics IO. You can also have physical I/O done as read-ahead, blob reads etc. Make sure you read the whole line and all numbers after the logical reads should be summed to get the total physical reads. Also, the perfmon buffer cache hit ratio counter don't include read-aheads, meaning it is very misleading.
The math here regarding your situation, how much memory is allocated to SQL Server, and how much data was read for this query is quite irrelevant. The relevant concepts of how SQL Server works are what you're missing.
SQL Server doesn't use memory to store every bit of data that is read for the entire duration of the query. If there is ample memory and the query doesn't use much memory, then it certainly can do that. But for queries that return a large result, require a large result to be sorted, or (insert reason for large amount of memory needed here), SQL Server will eventually utilize TempDB to store that data.
Most of the memory used by SQL Server is for buffering data pages to reduce the need to read from disk. Disk access, even on the fastest SSDs, is far slower than memory access. So for a single query that needs to use a gigabyte (for example) to sort the result, SQL Server uses tempdb instead of evicting a gigabyte of data from the buffer in order to sort it. If it did work that way, one bad query could run SQL Server out of memory and empty the buffer and performance in general would be dismal.