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/ 大家踊跃拍砖共同成长,拍砖的同时记得附上意见或者建议。!!谢谢 谢谢
  • 相关阅读:
    PAT 甲级 1027 Colors in Mars
    PAT 甲级 1026 Table Tennis(模拟)
    PAT 甲级 1025 PAT Ranking
    PAT 甲级 1024 Palindromic Number
    PAT 甲级 1023 Have Fun with Numbers
    PAT 甲级 1021 Deepest Root (并查集,树的遍历)
    Java实现 蓝桥杯VIP 算法训练 无权最长链
    Java实现 蓝桥杯VIP 算法训练 无权最长链
    Java实现 蓝桥杯 算法提高 抽卡游戏
    Java实现 蓝桥杯 算法提高 抽卡游戏
  • 原文地址:https://www.cnblogs.com/tianjing/p/2574577.html
Copyright © 2011-2022 走看看