zoukankan      html  css  js  c++  java
  • 开启LOH压缩?

    我们知道.NET CLR的GC堆中有一种特殊的堆,它专门存放超过85000byte的对象(详见这里),这就是大对象堆(LOH)。

    在.NET Framework 4.5.1之前,微软并没有提供对LOH的压缩操作,这是因为移动大对象的开销是很可观的。不能压缩LOH也会带来一些问题,比如LOH的内存碎片化。不过在.NET Framework 4.5.1种,微软加入了对LOH堆压缩的开关。对于为什么会加入这个开关,个人猜测应该是考虑到计算机性能足以支撑这种操作。具体使用参照以下代码:

    GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
    GC.Collect();    

    对于GCLargeObjectHeapCompactionMode.CompactOnce的解释是,在下一次回收gen2时压缩LOH。也就是说这个设置每执行一次只起一次效果,这种设计是很合理的,毕竟如果作为永久开关则会造成不必要的压缩操作。虽然计算机性能足以支持LOH的压缩操作,但这并不意味着不会影响性能。

    那么,我怎么知道这个操作达到了预期的效果呢?这就要上调试神器WinDbg了,虽然类似CLRProfiler也可以看出来,但就信息量来说WinDbg要胜一筹。

    首先上测试代码:

    class Program
    {
        static void Main(string[] args)
        {
            //在LOH堆上放一个大对象,调用完后该对象即被认作是垃圾
            MakeALohObject();
    
            //在LOH堆上再放一个大对象,此时该对象会被保持
            var bytes = new byte[1024*1024];
    
            //注释和取消注释以下开关,并分别dump内存用于分析
            GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
            //触发all gc
            GC.Collect();
    
            Console.Read();
        }
    
        static void MakeALohObject()
        {
            var obj = new byte[1024 * 1024];
        }
    }

    下面是未打开压缩LOH开关的dump信息节选:

    0:000> !eeheap
    ---------------省略部分信息
    Number of GC Heaps: 1
    generation 0 starts at 0x000001450b255b90
    generation 1 starts at 0x000001450b251018
    generation 2 starts at 0x000001450b251000
    ephemeral segment allocation context: none
             segment             begin         allocated              size
    000001450b250000  000001450b251000  000001450b255ba8  0x4ba8(19368)
    Large object heap starts at 0x000001451b251000
             segment             begin         allocated              size
    000001451b250000  000001451b251000  000001451b4599f0  0x2089f0(2132464)
    Total Size:              Size: 0x20d598 (2151832) bytes.
    ------------------------------
    GC Heap Size:            Size: 0x20d598 (2151832) bytes.
    
    
    
    0:000> !dumpheap -stat
    Statistics:
                  MT    Count    TotalSize Class Name
    ----------------省略部分信息
    00007ff8edfa2160        2          706 System.Char[]
    00007ff8edfa61a8        2         1072 System.Globalization.CultureData
    00007ff8edfa2360       17         1112 System.String[]
    00007ff8edfa2f10       26         1456 System.RuntimeType
    00007ff8edfa1010      154         6696 System.String
    00007ff8edfa1688        5        35144 System.Object[]
    00007ff8edfa7248        3      1048904 System.Byte[]
    00000145095abf40       24      1051068      Free
    Total 294 objects
    
    
    
    0:000> !DumpHeap /d -mt 00000145095abf40
             Address               MT     Size
    000001450b251000 00000145095abf40       24 Free
    000001450b251018 00000145095abf40       24 Free
    000001450b251030 00000145095abf40       24 Free
    ----------------省略部分信息
    000001451b259980 00000145095abf40  1048662 Free

    从!eeheap命令结果可以看出LOH共2132464byte,约2mb,也就是说虽然回收了一个1mb的byte数组,但是内存并未压缩。从!dumpheap -stat中的Free里面可以找到被释放的1mb空间。

    接下来看看打开压缩LOH开关的内存dump:

    0:000> !eeheap
    ----------------省略部分信息
    Number of GC Heaps: 1
    generation 0 starts at 0x0000015300005390
    generation 1 starts at 0x0000015300001018
    generation 2 starts at 0x0000015300001000
    ephemeral segment allocation context: none
             segment             begin         allocated              size
    0000015300000000  0000015300001000  00000153000053a8  0x43a8(17320)
    Large object heap starts at 0x0000015310001000
             segment             begin         allocated              size
    0000015310000000  0000015310001000  00000153101099b8  0x1089b8(1083832)
    Total Size:              Size: 0x10cd60 (1101152) bytes.
    ------------------------------
    GC Heap Size:            Size: 0x10cd60 (1101152) bytes.
    
    
    
    0:000> !dumpheap -stat
    Statistics:
                  MT    Count    TotalSize Class Name
    ----------------省略部分信息
    000001536ca7be30       16          414      Free
    00007ff8edfa8520        1          432 System.Collections.Generic.Dictionary`2+Entry[[System.Type, mscorlib],[System.Security.Policy.EvidenceTypeDescriptor, mscorlib]][]
    00007ff8edfa39d8        6          524 System.Int32[]
    00007ff8edfa2160        2          706 System.Char[]
    00007ff8edfa61a8        2         1072 System.Globalization.CultureData
    00007ff8edfa2360       17         1112 System.String[]
    00007ff8edfa2f10       26         1456 System.RuntimeType
    00007ff8edfa1010      154         6696 System.String
    00007ff8edfa1688        5        35144 System.Object[]
    00007ff8edfa7248        3      1048904 System.Byte[]
    Total 286 objects
    
    从上面可以看出LOH的size未1083832,即约1mb,这说明的确是将LOH压缩了,且!dumpheap -stat里的Free里也找不到这块空区域了。
    说了这么多其实都不是重点,重点是既然我们用了LOH压缩,那么LOH压缩对性能会产生多大影响呢?首先我们要明白,启用LOH压缩并不是直接影响我们所编写的代码的执行性能,而是影响的GC回收性能。而GC回收的前奏就是挂起所有工作线程,所以GC每次执行的时间决定着整个系统将挂起多长时间。
    那么接着贴一段代码,该段代码用于统计在启用和关闭LOH压缩的平均耗时:
     
    class Program
    {
        static void Main(string[] args)
        {
            var cycle = 1000;
            var total = 0l;
            //预热
            MakeGrabage();
    
            for (int j = 0; j < cycle; j++)
            {
                total += MakeGrabage();
            }
            Console.WriteLine("平均耗时{0}毫秒", total / (double)cycle);
            Console.Read();
        }
    
        static long MakeGrabage()
        {
            var objs = new byte[1000][];
            //初始化1000个1mb的byte数组
            for (int i = 0; i < 1000; i++)
            {
                objs[i] = new byte[1024 * 1024];
            }
            //将其中一半置为垃圾对象
            for (int i = 0; i < objs.Length; i++)
            {
                if (i % 2 == 0)
                {
                    objs[i] = null;
                }
            }
            var sw = Stopwatch.StartNew();
            //开启或关闭LOH压缩
            GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
            GC.Collect();
            sw.Stop();
            var elasped = sw.ElapsedMilliseconds;
    
            objs = null;
            GC.Collect();
            return elasped;
        }
    }
    在上述条件下,我的机器跑出的结果是:不开启LOH压缩平均回收需要0.705毫秒,而开启后平均回收需要325.369毫秒,差了461倍。内存占用情况不开启LOH压缩平均在1400mb左右,开启后平均在850mb左右,内存是纯眼看,所以可能差距比较大。
    就上述数据而言,LOH压缩还是在有必要的时候再用吧。

    原创文章,转载请注明: 转载自xdlysk的博客

    本文链接地址: 开启LOH压缩?[http://www.xdlysk.com/article/5826c1d2b16bc40409d5cac8]

  • 相关阅读:
    docker下安装mysql数据库
    asp.net core3.0 mvc 用 autofac
    遍历Map的方式
    JAVA 每次从List中取出100条记录
    JAVA 必须掌握技能(三)-Java 基础知识
    JAVA 必须掌握技能-Java 知识结构图
    JAVA 必须掌握技能(二)-Java IO流学习之输入输出流
    JAVA 必须掌握技能(一)-集合类型那么多,如何选择使用List, Set, Map?
    JavaScript 开发必须掌握技能(四)- 更好的使用jQuery attr方法
    JavaScript 开发必须掌握技能(三)- 更好的使用for循环方法
  • 原文地址:https://www.cnblogs.com/xdlysk/p/6057081.html
Copyright © 2011-2022 走看看