值类型与引用类型(转)

1. 引言

买了新本本,忙了好几天系统,终于开始了对值类型和引用类型做个全面的讲述了,本系列开篇之时就是因为想写这个主题,才有了写个系列的想法。所以对值类型和引用类型的分析,是我最想成文的一篇,其原因是过去的学习过程中我就是从这个主题开始,喜欢以IL语言来分析执行,也喜好从底层的过程来深入了解。这对我来说,似乎是一件找到了有效提高的方法,所以想写的冲动就没有停过,旨在以有效的方式来分享所得。同时,我也认为,对值类型和引用类型的把握,是理解语言基础环节的关键主题,有必要花力气来了解和深入。  

2. 一切从内存开始

2.1 基本概念

从上回《第七回:品味类型---从通用类型系统开始》我们知道,CLR支持两种基本类型:值类型引用类型。因此,还是把MSDN这张经典视图拿出来做个铺垫。

 

值类型(Value Type),值类型实例通常分配在线程的堆栈(stack)上,并且不包含任何指向实例数据的指针,因为变量本身就包含了其实例数据。其在MSDN的定义为值类型直接包含它们的数据,值类型的实例要么在堆栈上,要么内联在结构中。我们由上图可知,值类型主要包括简单类型、结构体类型和枚举类型等。通常声明为以下类型:int、char、float、long、bool、double、struct、enum、short、byte、decimal、sbyte、uint、ulong、ushort等时,该变量即为值类型。  

引用类型(Reference Type),引用类型实例分配在托管堆(managed heap)上,变量保存了实例数据的内存引用。其在MSDN中的定义为引用类型存储对值的内存地址的引用,位于堆上。我们由上图可知,引用类型可以是自描述类型、指针类型或接口类型。而自描述类型进一步细分成数组和类类型。类类型是则可以是用户定义的类、装箱的值类型和委托。通常声明为以下类型:class、interface、delegate、object、string以及其他的自定义引用类型时,该变量即为引用类型。

下面简单的列出我们类型的进一步细分,数据来自MSDN,为的是给我们的概念中有清晰的类型概念,这是最基础也是最必须的内容。

  

2.2 内存深入

2.2.1. 内存机制

那么.NET的内存分配机制如何呢?

数据在内存中的分配位置,取决于该变量的数据类型。由上可知,值类型通常分配在线程的堆栈上,而引用类型通常分配在托管堆上,由GC来控制其回收。例如,现在有MyStruct和MyClass分别代表一个结构体和一个类,如下:

using System;

public class Test
{
    
static void Main()
    {
        
//定义值类型和引用类型,并完成初始化
        MyStruct myStruct = new MyStruct();
        MyClass myClass = 
new MyClass();
        
        
//定义另一个值类型和引用类型,
        //以便了解其内存区别
        MyStruct myStruct2 = new MyStruct();
        myStruct2 = myStruct;
        
        MyClass myClass2 = 
new MyClass();
        myClass2 = myClass;        
    }
}

在上述的过程中,我们分别定义了值类型变量myStruct和引用类型变量myClass,并使用new操作符完成内存分配和初始化操作,此处new的区别可以详见《第五回:深入浅出关键字---把new说透》 的论述,在此不做进一步描述。而我们在此强调的是myStruct和myClass两个变量在内存分配方面的区别,还是以一个简明的图来展示一下:

 

我们知道,每个变量或者程序都有其堆栈,不同的变量不能共有同一个堆栈地址,因此myStruct和myStruct2在堆栈中一定占用了不同的堆栈地址,尽管经过了变量的传递,实际的内存还是分配在不同的地址上,如果我们再对myStruct2变量改变时,显然不会影响到myStruct的数据。从图中我们还可以显而易见的看出,myStruct在堆栈中包含其实例数据,而myClass在堆栈中只是保存了其实例数据的引用地址,实际的数据保存在托管堆中。因此,就有可能不同的变量保存了同一地址的数据引用,当数据从一个引用类型变量传递到另一个相同类型的引用类型变量时,传递的是其引用地址而不是实际的数据,因此一个变量的改变会影响另一个变量的值。从上面的分析就可以明白的知道这样一个简单的道理:值类型和引用类型在内存中的分配区别是决定其应用不同的根本原因,由此我们就可以很容易的解释为什么参数传递时,按值传递不会改变形参值,而按址传递会改变行参的值,道理正在于此。 

对于内存分配的更详细位置,可以描述如下:

·         值类型变量做为局部变量时,该实例将被创建在堆栈上;而如果值类型变量作为类型的成员变量时,它将作为类型实例数据的一部分,同该类型的其他字段都保存在托管堆上,这点我们将在接下来的嵌套结构部分来详细说明。

·         引用类型变量数据保存在托管堆上,但是根据实例的大小有所区别,如下:如果实例的大小小于85000Byte时,则该实例将创建在GC堆上;而当实例大小大于等于85000byte时,则该实例创建在LOH(Large Object Heap)堆上。

更详细的分析,我推荐《类型实例的创建位置、托管对象在托管堆上的结构》。

2.2.2. 嵌套结构 

嵌套结构就是在值类型中嵌套定义了引用类型,或者在引用类型变量中嵌套定义了值类型,相信园子中关于这一话题的论述和关注都不是很多。因此我们很有必要发挥一下,在此就顺藤摸瓜,从上文对.NET的内存机制着手来理解会水到渠成。

·         引用类型嵌套值类型

值类型如果嵌套在引用类型时,也就是值类型在内联的结构中时,其内存分配是什么样子呢? 其实很简单,例如类的私有字段如果为值类型,那它作为引用类型实例的一部分,也分配在托管堆上。例如:

public class NestedValueinRef

  
//aInt做为引用类型的一部分将分配在托管堆上 
  private int aInt;  
  
public NestedValueinRef 
  { 
    
//aChar则分配在该段代码的线程栈上 
     char achar = 'a'; 
  } 

其内存分配图可以表示为:

  

·          值类型嵌套引用类型

引用类型嵌套在值类型时,内存的分配情况为:该引用类型将作为值类型的成员变量,堆栈上将保存该成员的引用,而成员的实际数据还是保存在托管堆中。例如:

public struct NestedRefinValue
{
    
public MyClass myClass;
    
public NestedRefinValue
    {
        myClass.X = 1;
        myClass.Y = 2;
    }
}

其内存分配图可以表示为:

 

2.2.3. 一个简单的讨论

通过上面的分析,如果我们现在有如下的执行时:

AType[] myType = new AType[10];

试问:如果AType是值类型,则分配了多少内存;而如果AType是引用类型时,又分配了多少内存?

我们的分析如下:根据CRL的内存机制,我们知道如果ATpye为Int32类型,则表示其元素是值类型,而数组本身为引用类型,myType将保存指向托管堆中的一块大小为4×10byte的内存地址,并且将所有的元素赋值为0;而如果AType为自定义的引用类型,则会只做一次内存分配,在线程的堆栈创建了一个指向托管堆的引用,而所有的元素被设置为null值,表示为空。 

未完,下回即将发布。。。

1. 引言

上回[第八回:品味类型---值类型与引用类型(上)-内存有理]的发布,受到大家的不少关注,我们从内存的角度了解了值类型和引用类型的所以然,留下的任务当然是如何应用类型的不同特点在系统设计、性能优化等方面发挥其作用。因此,本回是对上回有力的补充,同时应朋友的希望,我们尽力从内存调试的角度来着眼一些设计的分析,这样就有助于对这一主题进行透彻和全面的理解,当然这也是下一回的重点。

从内存角度来讨论值类型和引用类型是有理有据的,  而从规则的角度来了解值类型和引用类型是无边无际的。本文旨在从上文呼应的角度,来把这个主题彻底的融会贯通,无边无迹的应用,还是来自反复无常的实践,因此对应用我只能说以一个角度来阐释观点,但是肯定不可能力求全局。因此,我们从以下几个角度来完成对值类型与引用类型应用领域的讨论。 

2. 通用规则与比较

通用有规则:

·         string类型是个特殊的引用类型,它继承自System.Object肯定是个引用类型,但是在应用表现上又凸现出值类型的特点,那么究竟是什么原因呢?例如有如下的一段执行:

 

 简单的说是由于string的immutable特性,因此每次对string的改变都会在托管堆中产生一个新的string变量,上述string作为参数传递时,实际上执行了s=s操作,在托管堆中会产生一个新的空间,并执行数据拷贝,所以才有了类似于按值传递的结果。但是根据我们的内存分析可知,string在本质上还是一个引用类型,在参数传递时发生的还是按址传递,不过由于其特殊的恒定特性,在函数内部新建了一个string对象并完成初始化,但是函数外部取不到这个变化的结果,因此对外表现的特性就类似于按值传递。至于string类型的特殊性解释,我推荐Artech的大作《深入理解string和如何高效地使用string》。

另外,string类型重载了==操作符,在类型比较是比较的是实际的字符串,而不是引用地址,因此有以下的执行结果:

            string aString = "123";
            
string bString = "123";
            Console.WriteLine((aString == bString)); 
//显示为true,等价于aString.Equals(bString);
            string cString = bString;
            cString = "456";
            Console.WriteLine((bString == cString)); 
//显示为false,等价于bString.Equals(cString);

·         通常可以使用Type.IsValueType来判断一个变量的类型是否为值类型,典型的操作为: 

    public struct MyStructTester
    { }

    
public class isValueType_Test
    {
        
public static void Main()
        {
            MyStructTester aStruct = 
new MyStructTester();
            Type type = aStruct.GetType();
            
if (type.IsValueType)
            {
                Console.WriteLine("{0} belongs to value type.", aStruct.ToString());
            }
 
        }
    }

·         .NET中以操作符ref和out来标识值类型按引用类型方式传递,其中区别是:ref在参数传递之前必须初始化;而out则在传递前不必初始化,且在传递时必须显式赋值。

·         值类型与引用类型之间的转换过程称为装箱与拆箱,这值得我们以专门的篇幅来讨论,因此留待后文详细讨论这一主题。

·         sizeof()运算符用于获取值类型的大小,但是不适用于引用类型。

·         值类型使用new操作符完成初始化,例如:MyStruct aTest = new MyStruct(); 而单纯的定义没有完成初始化动作,此时对成员的引用将不能通过编译,例如: 

MyStruct aTest;
Console.WriteLine(aTest.X); 

·         引用类型在性能上欠于值类型主要是因为以下几个方面:引用类型变量要分配于托管堆上;内存释放则由GC完成,造成一定的CG堆压力;同时必须完成对其附加成员的内存分配过程;以及对象访问问题。因此,.NET系统不能由纯粹的引用类型来统治,性能和空间更加优越和易于管理的值类型有其一席之地,这样我们就不会因为一个简单的byte类型而进行复杂的内存分配和释放工作。Richter就称值类型为“轻量级”类型,简直恰如其分,处理数据较小的情况时,应该优先考虑值类型。

·         值类型都继承自System.ValueType,而System.ValueType又继承自System.Object,其主要区别是ValueType重写了Equals方法,实现对值类型按照实例值比较而不是引用地址来比较,具体为:

char a = 'c';
char b = 'c';
Console.WriteLine((a.Equals(b))); 
//会返回true; 

·         基元类型,是指编译器直接支持的类型,其概念其实是针对具体编程语言而言的,例如C#或者VB.NET,通常对应用.NET Framework定义的内置值类型。这是概念上的界限,不可混淆。例如:int对应于System.Int32,float对应于System.Single。

比较出真知:

·         值类型继承自ValueType(注意:而System.ValueType又继承自System.Object);而引用类型继承自System.Object。

·         值类型变量包含其实例数据,每个变量保存了其本身的数据拷贝(副本),因此在默认情况下,值类型的参数传递不会影响参数本身;而引用类型变量保存了其数据的引用地址,因此以引用方式进行参数传递时会影响到参数本身,因为两个变量会引用了内存中的同一块地址。

·         值类型有两种表示:装箱与拆箱;引用类型只有装箱一种形式。我会在下节以专门的篇幅来深入讨论这个话题。

·         典型的值类型为:struct,enum以及大量的内置值类型;而能称为类的都可以说是引用类型。 struct和class主要的区别可以参见我的拙作《第四回:后来居上:class和struct》来详细了解,也是对值类型和引用类型在应用方面的有力补充。

·         值类型的内存不由GC(垃圾回收,Gabage Collection)控制,作用域结束时,值类型会自行释放,减少了托管堆的压力,因此具有性能上的优势。例如,通常struct比class更高效;而引用类型的内存回收,由GC来完成,微软甚至建议用户最好不要自行释放内存。

·         值类型是密封的(sealed),因此值类型不能作为其他任何类型的基类,但是可以单继承或者多继承接口;而引用类型一般都有继承性。 

·         值类型不具有多态性;而引用类型有多态性。

·         值类型变量不可为null值,值类型都会自行初始化为0值;而引用类型变量默认情况下,创建为null值,表示没有指向任何托管堆的引用地址。对值为null的引用类型的任何操作,都会抛出NullReferenceException异常。

·         值类型有两种状态:装箱和未装箱,运行库提供了所有值类型的已装箱形式;而引用类型通常只有一种形式:装箱。

3. 对症下药-应用场合与注意事项

现在,在内存机制了解和通用规则熟悉的基础上,我们就可以很好的总结出值类型和引用类型在系统设计时,如何作出选择?当然我们的重点是告诉你,如何去选择使用值类型,因为引用类型才是.NET的主体,不必花太多的关照就可以赢得市场。

3.1 值类型的应用场合

·         MSDN中建议以类型的大小作为选择值类型或者引用类型的决定性因素。数据较小的场合,最好考虑以值类型来实现可以改善系统性能;

·         结构简单,不必多态的情况下,值类型是较好的选择;

·         类型的性质不表现出行为时,不必以类来实现,那么用以存储数据为主要目的的情况下,值类型是优先的选择;

·         参数传递时,值类型默认情况下传递的是实例数据,而不是内存地址,因此数据传递情况下的选择,取决于函数内部的实现逻辑。值类型可以有高效的内存支持,并且在不暴露内部结构的情况下返回实例数据的副本,从安全性上可以考虑值类型,但是过多的值传递也会损伤性能的优化,应适当选择;

·         值类型没有继承性,如果类型的选择没有子类继承的必要,优先考虑值类型;

·         在可能会引起装箱与拆箱操作的集合或者队列中,值类型不是很好的选择,因为会引起对值类型的装箱操作,导致额外内存的分配,例如在Hashtable。关于这点我将在后续的主题中重点讨论。 

3.2 引用类型的应用场合

·         可以简单的说,引用类型是.NET世界的全值杀手,我们可以说.NET世界就是由类构成的,类是面向对象的基本概念,也是程序框架的基本要素,因此灵活的数据封装特性使得引用类型成为主流;

·         引用类型适用于结构复杂,有继承、有多态,突出行为的场合;

·         参数传递情况也是考虑的必要因素;    

4. 再论类型判等

类型的比较通常有Equals()、ReferenceEquals()和==/!=三种常见的方法,其中核心的方法是Equals。我们知道Equals是System.Object提供的虚方法,用于比较两个对象是否指向相同的引用地址,.NET Framework的很多类型都实现了对Equals方法的重写,例如值类型的“始祖”System.ValueType就重载了Equal方法,以实现对实例数据的判等。因此,类型的判等也要从重写或者重载Equals等不同的情况具体分析,对值类型和引用类型判等,这三个方法各有区别,应多加注意。

4.1 值类型判等

·         Equals,System.ValueType重载了System.Object的Equals方法,用于实现对实例数据的判等。

·         ReferenceEquals,对值类型应用ReferenceEquals将永远返回false。

·         ==,未重载的==的值类型,将比较两个值是否“按位”相等。

4.2 引用类型判等 

·         Equals,主要有两种方法,如下  

public virtual bool Equals(object obj);
public static bool Equals(object objA, object objB);

 一种是虚方法,默认为引用地址比较;而静态方法,如果objA是与objB相同的实例,或者如果两者均为空引用,或者如果objA.Equals(objB)返回true,则为true;否则为false。.NET的大部分类都重写了Equals方法,因此判等的返回值要根据具体的重写情况决定。 

·         ReferenceEquals,静态方法,只能用于引用类型,用于比较两个实例对象是否指向同一引用地址。

·         ==,默认为引用地址比较,通常进行实现了==的重载,未重载==的引用类型将比较两个对象是否引用地址,等同于引用类型的Equals方法。因此,很多的.NET类实现了对==操作符的重载,例如System.String的==操作符就是比较两个字符串是否相同。而==和equals方法的主要区别,在于多态表现上,==是被重载,而Equals是重写。

有必要在自定义的类型中,实现对Equals和==的重写或者重载,以提高性能和针对性分析。 

5. 再论类型转换

类型转换是引起系统异常一个重要的因素之一,因此在有必要在这个主题里做以简单的总结,我们不力求照顾全面,但是追去提纲挈领。常见的类型转换包括:

·         隐式转换:由低级类型项高级类型的转换过程。主要包括:值类型的隐式转换,主要是数值类型等基本类型的隐式转换;引用类型的隐式转换,主要是派生类向基类的转换;值类型和引用类型的隐士转换,主要指装箱和拆箱转换。

·         显示转换:也叫强制类型转换。但是转换过程不能保证数据的完整性,可能引起一定的精度损失或者引起不可知的异常发生。转换的格式为, 

(type)(变量、表达式)

例如:int a = (int)(b + 2.02);

·         值类型与引用类型的装箱与拆箱是.NET中最重要的类型转换,不恰当的转换操作会引起性能的极大损耗,因此我们将以专门的主题来讨论。

·         以is和as操作符进行类型的安全转换,详见本人拙作《第一回:恩怨情仇:is和as》。

·         System.Convert类定义了完成基本类型转换的便捷实现。

·         除了string以外的其他类型都有Parse方法,用于将字符串类型转换为对应的基本类型;

·         使用explicit或者implicit进行用户自定义类型转换,主要给用户提高自定义的类型转换实现方式,以实现更有目的的转换操作,转换格式为,

static 访问修饰操作符 转换修饰操作符 operator 类型(参数列表);

 例如:

public Student
{
    
//
    
    
static public explicite opertator Student(string name, int age)
    {
        
return new Student(name, age);
    }

    
//
}

其中,所有的转换都必须是static的。  

6. 结论

现在,我们从几个角度延伸了上回对值类型和引用类型的分析,正如本文开头所言,对类型的把握还有很多可以挖掘的要点,但是以偏求全的办法我认为还是可取的,尤其是在技术探求的过程中,力求面面俱到的做法并不是好事。以上的几个角度,我认为是对值类型和引用类型把握的必经之路,否则在实际的系统开发中常常会在细小的地方栽跟头,摸不着头脑。

品味类型,我们以应用为要点撬开值类型和引用类型的规矩与方圆。

品味类型,我们将以示例为导航,开动一个层面的深入分析,下回《第十回:品味类型---值类型与引用类型(下)-应用征途》我们再见。

 

 

1. 引言

值类型与引用类型的话题经过了两个回合([第八回:品味类型---值类型与引用类型(上)-内存有理]和[第九回:品味类型---值类型与引用类型(中)-规则无边])的讨论和切磋,我们就基本的理解层面来说已经差不多了,但是对这一部分的进一步把握和更深刻的理解还要继续和深化,因为我自己就在两篇发布之际,我就得到装配脑袋兄的不倦指导,之后又查阅了很多的资料发现类型在.NET或者说语言基础中何其重要的内涵和深度,因此关于这个话题的讨论还没有停止,以后我将继续分享自己的所得与所感。

不过作为一个阶段,本文将值类型和引用类型的讨论从应用示例角度来进一步做以延伸,可以看作是对前两回的补充性探讨。我们从类型定义、实例创建、参数传递、类型判等、垃圾回收等几个方面来简要的对上两回的内容做以剖析,并以一定的IL语言和内存机制来说明,期望进一步加深我们的理解和分析。 

2. 以代码剖析

下面,我们以一个经典的值类型和引用类型对比的示例来剖析,其区别和实质。在剖析的过程中,我们主要以执行分析(主要是代码注释)、内存分析(主要是图例说明)和IL分析(主要是IL代码简析)三个方面来逐知识点解析,最后再做以总结描述,这样就可以有更深的理解。

2.1 类型定义

定义简单的值类型MyStruct和引用类型MyClass,在后面的示例中将逐渐完善,完整的代码可以点击下载[类型示例代码]。我们的讨论现在开始,

·         代码演示

    // 01 定义值类型
    public struct MyStruct
    {
        
private int _myNo;

        
public int MyNo
        {
            
get { return _myNo; }
            
set { _myNo = value; }
        }

        
public MyStruct(int myNo)
        {
            _myNo = myNo;
        }

        
public void ShowNo()
        {
            Console.WriteLine(_myNo);
        }

    }

 

    // 02 定义引用类型
    public class MyClass
    {
        
private int _myNo;

        
public int MyNo
        {
            
get { return _myNo; }
            
set { _myNo = value; }
        }

        
public MyClass()
        {
            _myNo = 0;
        }

        
public MyClass(int myNo)
        {
            _myNo = myNo;
        }


        
public void ShowNo()
        {
            Console.WriteLine(_myNo);
        }
    }

·          IL分析

 

分析IL代码可知,静态方法.ctor用来表示实现构造方法的定义,其中该段IL代码表示将0赋给字段_myNo。

2.2 创建实例、初始化及赋值

接下来,我们完成实例创建和初始化,和简单的赋值操作,然后在内存和IL分析中发现其实质。

·         代码演示

创建实例、初始化及赋值创建了值为等价"0"的实例
成员

·         内存实况

首先是值类型和引用类型的定义,这是一切面向对象的开始,

  

 然后是初始化过程,

 

 简单的赋值和拷贝,是最基本的内存操作,不妨看看,

  

2.3 参数传递

·         代码演示

 

参数传递方式传递
方式传递,不必初始化

 

不必多说,就是一个简要阐释,对于参数的传递作者将计划以更多的笔墨来在后面的系列中做以澄清和深入。

2.4 类型转换

类型转换的演示,包括很多个方面,在此我们只以自定义类型转换为例来做以说明,更详细的类型转换可以参考[第九回:品味类型---值类型与引用类型(中)-规则无边]的[再论类型转换部分]。

·         代码演示

首先是值类型的自定义类型转换,

 

    public struct MyStruct
    {        
        
// 01.2 自定义类型转:整形->MyStruct型
        static public explicit operator MyStruct(int myNo)
        {
            
return new MyStruct(myNo);
        }

    }


然后是引用类型的自定义类型转换,

 

    public class MyClass
    {
        
// 02.2 自定义类型转换:MyClass->string型
        static public implicit operator string(MyClass mc)
        {
            
return mc.ToString();
        }

        
public override string ToString()
        {
            
return _myNo.ToString();
        }
    }


最后,我们对自定义的类型做以测试,

        public static void Main(string[] args)
        {          
            
#region 03. 类型转换
            MyStruct MyNum;
            
int i = 100;
            MyNum = (MyStruct)i;
            Console.WriteLine("
整形显式转换为MyStruct型---");
            Console.WriteLine(i);

            MyClass MyCls = 
new MyClass(200);
            
string str = MyCls;
            Console.WriteLine("MyClass
型隐式转换为string型---");
            Console.WriteLine(str);
            
#endregion                   
                   }       

2.5 类型判等

类型判等主要包括:ReferenceEquals()、Equals()虚方法和静态方法、==操作符等方面,同时注意在值类型和引用类型判等时的不同之处,可以参考[第九回:品味类型---值类型与引用类型(中)-规则无边]的[4. 再论类型判等]的简述。

·         代码演示 

// 01 定义值类型
public struct MyStruct
{

// 01.1 值类型的类型判等
public override bool Equals(object obj)
{
return base.Equals(obj);
}

}  

public class MyClass
{

// 02.1 引用类型的类型判等
public override bool Equals(object obj)
{
return base.Equals(obj);
}

public static void Main(string[] args)

{

#region 05 类型判等
Console.WriteLine("类型判等---");
// 05.1 ReferenceEquals判等
//值类型总是返回false,经过两次装箱的myStruct不可能指向同一地址
Console.WriteLine(ReferenceEquals(myStruct, myStruct));
//同一引用类型对象,将指向同样的内存地址
Console.WriteLine(ReferenceEquals(myClass, myClass));
//RefenceEquals认为null等于null,因此返回true
Console.WriteLine(ReferenceEquals(null, null));

// 05.2 Equals判等
//重载的值类型判等方法,成员大小不同
Console.WriteLine(myStruct.Equals(myStruct2)) ;

//重载的引用类型判等方法,指向引用相同
Console.WriteLine(myClass.Equals(myClass2));

#endregion

}  

2.6 垃圾回收  

首先,垃圾回收机制,绝对不是三言两语就能交代清楚,分析明白的。因此,本示例只是从最简单的说明出发,对垃圾回收机制做以简单的分析,目的是有始有终的交代实例由创建到消亡的全过程。

·         代码演示

public static void Main(string[] args)
{

#region 06 垃圾回收的简单阐释
//实例定义及初始化
MyClass mc1 = new MyClass();
//声明但不实体化
MyClass mc2;
//拷贝引用,mc2和mc1指向同一托管地址
mc2 = mc1;
//定义另一实例,并完成初始化
MyClass mc3 = new MyClass();
//引用拷贝,mc1、mc2指向了新的托管地址
//那么原来的地址成为GC回收的对象,在
mc1 = mc3;
mc2 = mc3;
#endregion

}  

·         内存实况

 

GC执行时,会遍历所有的托管堆对象,按照一定的递归遍历算法找出所有的可达对象和不可访问对象,显然本示例中的托管堆A对象没有被任何引用访问,属于不可访问对象,将被列入执行垃圾收集的目标。对象由newobj指令产生,到被GC回收是一个复杂的过程,我们期望在系列的后期对此做以深入浅出的理解。 

2.7 总结陈述

这些示例主要从从基础的方向入手来剖析前前两回中的探讨,不求能够全面而深邃,但求能够一点而及面的展开,技术的魅力正在于千变万化,技术追求者的力求却是从变化中寻求不变,不然我们实质太累了,我想这就是好方法,本系列希望的就是提供一个入口,打开一个方法。示例的详细分析可以下载[类型示例代码],简单的分析希望能带来丝丝惬意。 

3. 结论

值类型和引用类型,要说的,要做的,还有很多。此篇只是一个阶段,更多的深入和探讨我相信还在继续,同时广泛的关注技术力量的成长,是每个人应该进取的空间和道路。

品味类型,为应用之路开辟技术基础。

品味类型,继续探讨还会更多精彩。

 

posted @ 2012-09-14 16:41  海燕一家  阅读(302)  评论(0编辑  收藏  举报