zoukankan      html  css  js  c++  java
  • 值类型 引用类型 堆栈 堆 之 异想

    看了很多值类型 和 引用类型的文章(谷歌能搜索出来的)
    看了越多疑问越大,而这些资料中没有具体的说明。
    问题:
    1、堆栈 和 堆 分别存于计算机的哪个硬件(CPU缓存,内存,硬盘)?
    2、为什么只有值类型和引用类型?
    3、为什么说引用类型是指针?
    4、堆栈必堆小小多少?
     
    以下是个人的分析,不是权威结果。
    1、堆栈 和 堆 分别存于计算机的哪个硬件(CPU缓存,内存,硬盘)?
    使用排除法来看这个问题
        (1)CPU缓存
        (2)内存
        (3)硬盘
            
        (3)可以排除堆栈的可能,因为 硬盘最慢
        (2)最有可能存堆栈,因为 速度适中,且相对来说存储空间足够大
        (1)可能性很小,应为仅几年来CPU的缓存越来越大 但目前家用级别的CPU的1,2,3级缓存很少超过10MB(多核情况下每个核心分到的更少);
           
        真像可能就是堆栈和堆都是放在内存里的。
        
        那么为什么堆栈比堆快呢?
        个人认为这情况和hashtable与list等数据容器的差异,差不多。
        存取方式决定的。
        堆栈:只能存取值类型,且先进先出,不够的时候直接压栈(就像"向右看起"的命令一样) --简单快捷
        堆:首先,堆的分配模式会存在碎片,并不是连续性的(这里直的是多个对象,找到一个适合的内存空间就把对象放进去,就像家居摆放物件一样,有时候不贴个纸条的话,得找半天)。
        
    2、为什么只有值类型和引用类型?
           这个我觉得追溯到本源比较好解释,就是CPU只能进行数学计算。(看下汇编代码会好理解些)
            值类型:就是数字,CPU可以直接进行运算。
            引用类型:最终指向值类型的指针。object是指针,object的ToString的函数还是一个指针,ToString内有String类型还是指针,最终指向一个Char[] 字符集合
           (注,我对String的理解就是Char[])。
            所以对象无法直接进行运算,只能通过指针找到能运算的部分,再进行运算。这也就是为啥只有2个类型了,一个是值用于运算,一个是指针,指向需要运算的地方。
     
    3、为什么说引用类型是指针?
        由上可知,引用类型是指针必然性。
        一个Class内除了Int32等 值类型外其他皆是指针,委托,函数,函数内的对象,属性,事件 都是指针。
        根据这种特性,指针(引用类型)作为参数传递,出来的时候会根据函数内的改变而改变,而值要作为参数输入并输出的话就要ref了。
        (注: 个人发现 DateTime 作为参数时具有值类型的特征)
        
    4、堆栈必堆小小多少?
         未知,希望有知道的朋友能给出测试方法或者结果
         我的推测是既然是在内存,必然没有限制,除非人为的限制
         我使用线程测试内存上限时发现没有具体的上限。我的是64位+8G内存的笔记本以下是测试结果:(线程内分别创建class和sturct)
        X86: 一个应用程序只能达到1300多一点的线程,再也上不去了,提交内存约1440k
               class:运行稳定。sturct:大约2分钟 内存溢出
       X64:一个应用程序只能达到8000多的线程,提交内存约10000k(还能继续)
               class:运行稳定。sturct:行稳定
        
     
    最终 我的结论:在C#里class 和sturct 如果真的是一个分配在堆,一个分配在堆栈,那么堆栈和堆的空间大小没有区别,只存在速度的区别
            
    以下是测试代码:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    namespace ConsoleApplication1
    {
    class Program
    {
    static void Main(string[] args)
    {
    for (var i = 0; i < 10000; i++)
    {
    Thread th = new Thread(() =>
    {
    abc a = new abc(1);
    });
    th.Start();
    }
    Console.ReadKey();
    }
    }
    struct abc
    {
    public abc(Int32 x)
    {
    ds = String.Empty;
    Test();
    }
    String ds;
    private void Test()
    {
    while (true)
    {
    ds += "A";
    Thread.Sleep(1000);
    }
    }
    }
    class bc
    {
    public bc(Int32 x)
    {
    ds = String.Empty;
    Test();
    }
    String ds;
    private void Test()
    {
    while (true)
    {
    ds += "A";
    Thread.Sleep(1000);
    }
    }
    }
    }
     
     
     
    IL:

    class 的

    // 代码大小 28 (0x1c)
    .maxstack 8
    IL_0000: ldarg.0
    IL_0001: call instance void [mscorlib]System.Object::.ctor()
    IL_0006: nop
    IL_0007: nop
    IL_0008: ldarg.0
    IL_0009: ldsfld string [mscorlib]System.String::Empty
    IL_000e: stfld string ConsoleApplication1.bc::ds
    IL_0013: ldarg.0
    IL_0014: call instance void ConsoleApplication1.bc::Test()
    IL_0019: nop
    IL_001a: nop
    IL_001b: ret
    } // end of method bc::.ctor
     
    sturct 的
    // 代码大小 20 (0x14)
    .maxstack 8
    IL_0000: nop
    IL_0001: ldarg.0
    IL_0002: ldsfld string [mscorlib]System.String::Empty
    IL_0007: stfld string ConsoleApplication1.abc::ds
    IL_000c: ldarg.0
    IL_000d: call instance void ConsoleApplication1.abc::Test()
    IL_0012: nop
    IL_0013: ret
    } // end of method abc::.ctor
     
     
    堆栈容量测试:
    using System;
    using System.Threading;
    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                String txt = System.IO.File.ReadAllText("demo.txt");//一个3.11MB的文本
    
                    Thread th = new Thread(() =>
                    {
                        abc a = new abc(1);
                        for (var i = 0; i < 1000; i++)
                        {
                            a.ds += txt;
                        }
                    });
                    th.Start();
                
                Console.ReadKey();
            }
    
    
        }
    
        struct abc
        {
            public abc(Int32 x)
            {
                ds = String.Empty;
            }
           public String ds;
    
        }
        
    }

    测试结果:X64 能 提交内存3000K以上

     


    我的结论和想法是这样的:

    首先我是站在CPU的角度去思考的。

    1、堆栈 堆 可能都是一样的指针,他们本身只是数据容器。

    2、他们的区别在于存取方式不一致导致的存取速度不一样。

    3、堆栈 和堆 没有具体大小,除非人为设置,且很有可能由CLR或者编译器动态选择数据容器。(毕竟我只能看到IL,看不到先X86反编译汇编)

    4、值类型传参实为传值,引用类型传参实为传地址(指针),这也是堆栈和堆数据使用上的区别。CPU对堆栈的态度是拿来就用,对堆就是找到再用。



    我的开源的地址https://github.com/tianjing/ 大家踊跃拍砖共同成长,拍砖的同时记得附上意见或者建议。!!谢谢 谢谢
  • 相关阅读:
    C#练习代码
    docker部署gitlab
    vim 缩进设置
    css 初步学习笔记
    tomcat
    201871010107公海瑜《面向对象程序设计(java)》第一周学习总结 公海瑜
    发现JavaScript真强大
    今天看了一點ant的內容,受益匪浅
    一道趣味問題
    一個挺適合初學者的Ant教程
  • 原文地址:https://www.cnblogs.com/tianjing/p/2574577.html
Copyright © 2011-2022 走看看