none
c#静态方法和实例方法的内存分配问题 RRS feed

  • 问题

  • 请问各位大侠下面这段有关c#方法内存分配问题的论述是否有问题:

    大家都以为“ 静态方法在堆上分配内 存,实例方法在堆栈上

    事实上所有的方法都不可能在堆或者堆栈上分配内 存,方法作为代码是被加载到特殊的代码内存区域,这个内存区域是不可写的。

    方法占不占用更多内存,和它是不是static没什么关系。  
    因为字段是用来存储每个实例对象的信息的,所以字段会占有内存,并且因为每个实例对象的状态都不一致(至少不能认为它们是一致的),所以每个实例对象的所 以字段都会在内存中有一分拷贝,也因为这样你才能用它们来区分你现在操作的是哪个对象。  
    但方法不一样,不论有多少个实例对象,它的方法的代码都是一样的,所以只要有一份代码就够了。因此无论是static还是non-static的方法,都 只存在一份代码,也就是只占用一份内存空间。  
    同样的代码,为什么运行起来表现却不一样?这就依赖于方法所用的数据了。主要有两种数据来源,一种就是通过方法的参数传进来,另一种就是使用class的 成员变量的值……

    我本人认为实例方法是随对象产生才存在一份定义的拷贝。不知各位有何高见!

    2010年11月24日 3:59

答案

  • 楼主上面那一大段说法只能说是靠谱了,正确性很低。我猜想这可能是某一本书或者博客上的文字。。。

    我来具体解释一下吧。

    方法 (Method) 是一种类型定义,所以,它被存放在 Type Object 上,Type Object 是一个被分配在托管堆上的特殊类型,在同一个 AppDomain 中,每一个类型,都对应一个全局的 Type Object。每个引用类型的实例,都包含一个指向它的直接类型的 Type Object 的指针,每个 Type Object 也存在类似的指针,用来标识它的直接父类型的 Type Object。

    当调用静态方法时,CLR 会根据方法调用去寻找其对应的 Type Object,然后,把方法 JIT,JIT 之后的方法是本机代码,可以直接运行,然后,这部分代码被加载进入内存,方法的参数被加载进入当前执行栈,原来的执行上下文地址也被记录到执行栈;方法开始执行,执行完后,执行栈中的返回地址被读出,然后 CLR 利用本机跳转指令,跳转到该返回至继续执行。

    当调用实例方法时,CLR 会根据实例的 Type Object 指针找到对应的 Type Object,然后,把方法 JIT,JIT 之后的方法是本机代码,可以直接运行,然后,这部分代码被加载进入内存,该实例对象,以及方法的参数被加载进入当前执行栈 (实例对象永远是第一个参数,即 arg0,利用 ldarg0 指令进行读取),原来的执行上下文地址也被记录到执行栈;方法开始执行,执行完后,执行栈中的返回地址被读出,然后 CLR 利用本机跳转指令,跳转到该返回至继续执行。

    如果方法已经被 JIT 过,则不会被第二次 JIT。

    方法在 IL 中是以字节流的形式存在的,所以,它仍然会占据内存。

    方法 JIT 之后会被驻留在该进程的地址空间里面,因此,它也会在运行时占据内存。

    方法的元数据存放在程序集 MethodRef 以及 MethodDef 表中。

    定义在值类型上的实例方法就比较麻烦了,大家有兴趣可以想想它怎么执行的。因为值类型没有 Type Object 指针。

    如果值类型实现一个接口,在执行接口的方法实现的时候就更加麻烦了,大家也可以想想,欢迎讨论!

    最后,

    大家都以为“ 静态方法在堆上分配内 存,实例方法在堆栈上

    这句话完全不靠谱,不要被迷惑了。。。只要提到方法,它就一定在 Type Object 上,也就是被分配在托管堆上。


    Mark Zhou
    2010年11月24日 9:36
  • 方法不参与内存分配。JIT的时候直接生成机器码,之后映射到内存。

    实例方法比静态方法多一个this参数而已。



    The following is signature, not part of post
    Please mark the post answered your question as the answer, and mark other helpful posts as helpful, so they will appear differently to other users who are visiting your thread for the same problem.
    Visual C++ MVP
    2010年11月24日 4:57
    版主
  • 方法不占用记忆体(内存)...

    有呼叫才会开始使用记忆体,方法结束后便释放该方法里的资源(GC回收)

    static方法不需将类实体化便可使用,执行起来会比非static的方法快一些,非static的方法浪费掉的时间只是用在等待类别的实体化而已


    秘訣無它,唯勤而已 http://www.dotblogs.com.tw/yc421206/
    2010年11月24日 7:03

全部回复

  • 方法不参与内存分配。JIT的时候直接生成机器码,之后映射到内存。

    实例方法比静态方法多一个this参数而已。



    The following is signature, not part of post
    Please mark the post answered your question as the answer, and mark other helpful posts as helpful, so they will appear differently to other users who are visiting your thread for the same problem.
    Visual C++ MVP
    2010年11月24日 4:57
    版主
  • 方法不占用记忆体(内存)...

    有呼叫才会开始使用记忆体,方法结束后便释放该方法里的资源(GC回收)

    static方法不需将类实体化便可使用,执行起来会比非static的方法快一些,非static的方法浪费掉的时间只是用在等待类别的实体化而已


    秘訣無它,唯勤而已 http://www.dotblogs.com.tw/yc421206/
    2010年11月24日 7:03
  • 谢谢两位,另外,请专家指点,有无比较详细的技术资料地址或书籍可推荐,让小弟更系统的理解与钻研?
    2010年11月24日 8:29
  • 楼主上面那一大段说法只能说是靠谱了,正确性很低。我猜想这可能是某一本书或者博客上的文字。。。

    我来具体解释一下吧。

    方法 (Method) 是一种类型定义,所以,它被存放在 Type Object 上,Type Object 是一个被分配在托管堆上的特殊类型,在同一个 AppDomain 中,每一个类型,都对应一个全局的 Type Object。每个引用类型的实例,都包含一个指向它的直接类型的 Type Object 的指针,每个 Type Object 也存在类似的指针,用来标识它的直接父类型的 Type Object。

    当调用静态方法时,CLR 会根据方法调用去寻找其对应的 Type Object,然后,把方法 JIT,JIT 之后的方法是本机代码,可以直接运行,然后,这部分代码被加载进入内存,方法的参数被加载进入当前执行栈,原来的执行上下文地址也被记录到执行栈;方法开始执行,执行完后,执行栈中的返回地址被读出,然后 CLR 利用本机跳转指令,跳转到该返回至继续执行。

    当调用实例方法时,CLR 会根据实例的 Type Object 指针找到对应的 Type Object,然后,把方法 JIT,JIT 之后的方法是本机代码,可以直接运行,然后,这部分代码被加载进入内存,该实例对象,以及方法的参数被加载进入当前执行栈 (实例对象永远是第一个参数,即 arg0,利用 ldarg0 指令进行读取),原来的执行上下文地址也被记录到执行栈;方法开始执行,执行完后,执行栈中的返回地址被读出,然后 CLR 利用本机跳转指令,跳转到该返回至继续执行。

    如果方法已经被 JIT 过,则不会被第二次 JIT。

    方法在 IL 中是以字节流的形式存在的,所以,它仍然会占据内存。

    方法 JIT 之后会被驻留在该进程的地址空间里面,因此,它也会在运行时占据内存。

    方法的元数据存放在程序集 MethodRef 以及 MethodDef 表中。

    定义在值类型上的实例方法就比较麻烦了,大家有兴趣可以想想它怎么执行的。因为值类型没有 Type Object 指针。

    如果值类型实现一个接口,在执行接口的方法实现的时候就更加麻烦了,大家也可以想想,欢迎讨论!

    最后,

    大家都以为“ 静态方法在堆上分配内 存,实例方法在堆栈上

    这句话完全不靠谱,不要被迷惑了。。。只要提到方法,它就一定在 Type Object 上,也就是被分配在托管堆上。


    Mark Zhou
    2010年11月24日 9:36
  • 说在堆上应该也对, 应该是代码区把.反正不是栈."实例方法在堆栈上" 这个就完全不靠谱了.方法在调用时需要压栈. 

    2010年11月25日 5:08
  • C#入门不久,一直困惑这个问题:静态方法什么时候开始占用内存,使用完毕会不会释放内存,如果不是放内存,静态方法多了,会不会造成严重的后果(什么后果?我不会描述请大家给解释下,谢谢)

    2012年5月31日 1:50
  • 压栈的是调用者分配的参数,方法本身的代码不会跑到栈上去。



    The following is signature, not part of post
    Please mark the post answered your question as the answer, and mark other helpful posts as helpful, so they will appear differently to other users who are visiting your thread for the same problem.
    Visual C++ MVP

    2012年5月31日 2:21
    版主