A value type is allocated on the stack if it's a local variable in a method. If a value type is a member of a class, it will be allocated as part of the object's memory area on the heap.
A value type variable does not need any extra data to keep track of the type, as reference types does. The compiler always knows where the value type variables are and what their type is, so there is no extra data needed in addition to the actual data. An Int32 variable will always be four bytes.
A reference type is allocated on the heap, and it has a reference (or more) that points to it. The reference itself is actually a value type, so it will just be a pointer, the compiler keeps track of where it is and what type it is. The type of the reference doesn't have to be the same as the type of the object that it is pointing to, so the object needs extra information to keep track of the type. For example, an object reference pointing to an instance of the StringBuilder class:
object o = new StringBuilder();
Here the compiler keeps track of that type of the reference is object, so it will just be a pointer (4 bytes in a 32-bit application). The StringBuilder object is stored on the heap, and it has two extra pointers with it that keeps track of the actual type.
A value type can also be boxed, i.e. stored as an object on the heap. This occurs when you cast a value type to Object:
object p = 42;
This will allocate an object on the heap and copy the value of the integer into it. This object will need the extra type information to keep track of the type, so it will use 12 bytes on the heap instead of four (in a 32 bit application).