None of the answers or comments so far give an accurate or germane explanation of the relevant portion of the question. It is clear that there is no boxing when the string is passed to OutputToScreen. The relevant question is why there is no boxing when the string is produced by the call to ToString, a virtual method on object.
First off, let's consider the premise of the question. Your question is motivated by the assumption that you gain by avoiding boxing. That is, either (1) you box, and then call ToString, and then display the result, or (2) you skip the boxing, call ToString, and display the result. Though that does sound like a win, you have to consider the win in context. Converting to string and displaying to screen are both slow compared to boxing; trying to make the program faster by avoiding boxing is like trying to get your cable bill paid faster by running to the post office instead of walking.
Ultimately any performance question should be settled by trying it both ways and measuring the effect in the context of a performance metric relevant to the user.
However, the question is still of general interest even if the effect of avoiding boxing is irrelevant for performance reasons.
Your question seems to be predicated on an incorrect understanding of how boxing works:
doesn't boxing still occur when I box x to a string type?
There is no such thing as boxing "to a string type". An int can box to a boxed int. That's all that an int can box to. A double can box to a boxed double. And so on.
The real question of interest that you should be asking is:
Why does calling ToString(), a virtual method declared on object, not box int to object in order to pass an object as "this" to the call?
The answer is straightforward. When a struct provides an accessible implementation of a virtual method, and you call that virtual method on the struct, then the C# compiler knows that the virtual method is not overridden anywhere else, because structs are all sealed. Since the compiler knows exactly what virtual method is being called, and exactly what the "this" reference should be for that method, it can generate code that calls that method directly, without boxing and subsequently looking up the method in the vtable.
int does provide a public override of ToString, and therefore calling ToString on an int does not box the int, look up ToString in the vtable of a boxed int, and then call the int's ToString on the boxed int. The compiler skips the middleman and goes straight to the call.
Now, suppose the method is not virtual. GetType() is not virtual. When you call GetType() on an int, the compiler cannot reason like that. It knows that there is only one implementation of GetType(), that it is on System.Object, and that it expects an object. So it boxes.
Now that you understand all that, you can understand the behaviour of this crazy program fragment:
int? x = null;
Console.WriteLine(x.ToString());
Console.WriteLine(x.GetType());
What does that do, and why?
int? overrides ToString, so the call to ToString succeeds. But GetType is not virtual, so x is boxed. There are no boxed nullable value types; a null int? boxes to a null reference. Therefore this calls null.GetType() and throws a null reference exception. Boxing can hide in places you don't expect!
If you make up your own struct:
struct S { /* does not override ToString */ }
then a call to
S s = new S();
string str = s.ToString();
will box, because only the object version is available, and that takes an object as "this".
Coincidentally, a variation on your question was the subject of my blog this past Monday. See that article for more details.
http://blogs.msdn.com/b/ericlippert/archive/2011/03/14/to-box-or-not-to-box-that-is-the-question.aspx
What is the vtable?
The vtable is the mechanism whereby virtual methods are looked up at runtime. See this answer from yesterday for a sketch of how the vtable works:
Virtual Functions C#