Freezing and pulling 100% at a CPU typically means you're running circles while computing something and never finish. One such case is in the spawn food code:
/*Choses a position at random anywhere on the play grid, execpt for the edges or the space which the
* snake head occupies.*/
do { foodGridPosition = new Vector2Int(Random.Range(1, width - 1), Random.Range(1, height - 1)); }
while (snake.GetFullSnakeGridPositionList().IndexOf(foodGridPosition) != -1 || wallGrid.CheckSnakeHitWall(foodGridPosition));
If for some reason, the condition at the bottom is always true, this runs circles forever (programming bug or no space left for placing food).
I didn't check all the code, so there could be more such points where you try something and try again if it doesn't work, possibly forever.
Another possibility is that you endlessly recurse into a function. you call a function, and that eventually calls the same function again, possible through a sequence of other steps. Unless this calling itself (directly or indirectly) ends at some point, this also freezes, pulls 100% at a CPU, and often slowly increases memory until you totally filled all space that you have.
The first thing to do is find the spot where the problem is. You could for example in a do-while loop count the number of iteration in a loop, and reaching some large number, break out of the loop, failing to find a position. You likely want to log such a thing so you can see that it's happening.
Finding functions calling themselves is more complicated, but the principles are the same. Verify that whatever the situation, execution always ends after a finite number of computations.
Another thing you could do is test by making small changes, I saw you spawn a new food after eating one. So what happens if you don't spawn new food?
Of course, it could also be something with eating food, maybe your problem is in extending the snake more than 9 times. (Didn't look at that.) For a test, does it happen when you don't extend the snake, but spawn food?
It could also be time related, after X time, something triggers. As a test, change play style. Don't do the same thing every test, eg stop eating. Does that change behavior?
So by trying a few variations in the code or by different game play, you can try to find the global area where the problem might be. Then go in, and check very carefully that all code always ends. Think like an addicted gamer, look at the code, what paths in the code should I pick to avoid finishing the game? How does the code prevent me from never finishing?
EDIT: There is also finite but very slow performance to consider.
As an extreme example the above do-while loop. Say you have a field of 1000x1000, so 1e6 fields. They are all full, except 1 field. So while theoretically the loop will end, finding 1 position in a 1,000,000 by random trying might take a long while, on average 1,000,000 tries, but due to randomness, that can easily explode by 10x as many tries.
Anything where you loop to find something, consider how often you might fail to find the thing you're looking for. If that is a large number, it might be a problem.