不过,值变量有时候也会存储在堆里头。看看黄金规则二,值类型总是保存在被声明的地方。那么,如果值类型在方法(method)外部声明,同时本身又存在于引用类型内部时,那么就会被保存在堆里头。
在来一个例子:
如果我们有MyInt Class(引用类型):
public class MyInt
{
public int MyValue;
}
{
public int MyValue;
}
另外一个方法正在执行中:
public MyInt AddFive(int pValue)
{
MyInt result = new MyInt();
result.MyValue = pValue + 5;
return result;
}
{
MyInt result = new MyInt();
result.MyValue = pValue + 5;
return result;
}
跟前头一样,线程开始执行方法,参数被压栈:

这个时候开始好玩了:
因为MyInt是引用类型,因此保存在堆里头,通过栈里的指针去引用它。

AddFive执行完毕以后,我们开始清理栈顶:

这样我们就把MyInt对象当作孤儿留在了堆里头,因为栈中没有任何指针在引用它了!

这时候就是GC大显身手的时候啦!一旦我们程序达到某个特定的内存阀值(threshold)而我们需要更多堆空间时,GC就会开动。GC完全停止所有运行中的线程,找到所有堆里没有被引用的对象,然后像秋风扫落叶般的删除它们。GC重新组织所有滞留在内存中活动的对象,并调整堆栈中对其引用的地址。可以看到,从性能角度来说,这可真是很费时间。所以现在你是不是觉得注意一下堆栈的处理会有助于你写出高性能的代码呢?
OK,好了好了,很棒很棒,不过,这东西到底会如何来折磨俺们呢?
好问题。
当我们使用引用类型时,我们实际上是使用指向该对象的指针,而不是对象本身。当我们使用值类型时,我们使用的就是值本身。清楚还是不清楚?
最好还是来个例子吧。 如果我们执行以下方法:
public int ReturnValue()
{
int x = new int();
x = 3;
int y = new int();
y = x;
y = 4;
return x;
}
{
int x = new int();
x = 3;
int y = new int();
y = x;
y = 4;
return x;
}
我们得到3,够简单吧。
如果这样呢:
public class MyInt
{
public int MyValue;
}
public int ReturnValue2()
{
MyInt x = new MyInt();
x.MyValue = 3;
MyInt y = new MyInt();
y = x;
y.MyValue = 4;
return x.MyValue;
}
{
public int MyValue;
}
public int ReturnValue2()
{
MyInt x = new MyInt();
x.MyValue = 3;
MyInt y = new MyInt();
y = x;
y.MyValue = 4;
return x.MyValue;
}
那么我们将得到4。
为什么?
在第一个例子里所有事都按照预定计划行事:
public int ReturnValue()
{
int x = 3;
int y = x;
y = 4;
return x;
}
{
int x = 3;
int y = x;
y = 4;
return x;
}