内存迟迟下不去,可能你就差一个GC.Collect

一:背景

1. 讲故事

我们有一家top级的淘品牌店铺,为了后续的加速计算,在程序启动的时候灌入她家的核心数据到内存中,灌入完成后内存高达100G,虽然云上的机器内存有256G,然被这么划掉一半看着还是有一点心疼的,可怜那些被挤压的小啰啰程序????????????,本以为是那些List,HashSet,Dictionary需要动态扩容虚占了很多内存,也就没当一回事,后来过了一天发现内存回到了大概70多G,卧槽,不是所谓的集合虚占,而是GC没给我回收呀。。。

2. windbg验证一下

为了验证我的说法,我就不去生产抓这个庞然大物的dump了,去测试环境给大家抓一个,晚上清蒸。

!eeheap -gc 查看gc信息


0:000> !eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x0000019b0fc66b48
generation 1 starts at 0x0000019b0f73b138
generation 2 starts at 0x0000019a5da81000
ephemeral segment allocation context: nonesegment             begin         allocated              size
0000019a5da80000  0000019a5da81000  0000019a6da7ffb8  0xfffefb8(268431288)
0000019a00000000  0000019a00001000  0000019a0ffffe90  0xfffee90(268430992)
0000019a10000000  0000019a10001000  0000019a1ffffeb0  0xfffeeb0(268431024)
0000019a20000000  0000019a20001000  0000019a2fffffb0  0xfffefb0(268431280)
0000019a30000000  0000019a30001000  0000019a3ffffc50  0xfffec50(268430416)
0000019a40000000  0000019a40001000  0000019a4fffffc8  0xfffefc8(268431304)
0000019a7aad0000  0000019a7aad1000  0000019a8aacfd60  0xfffed60(268430688)
0000019a8cbf0000  0000019a8cbf1000  0000019a9cbefe10  0xfffee10(268430864)
0000019a9cbf0000  0000019a9cbf1000  0000019aacbefcb8  0xfffecb8(268430520)
0000019aacbf0000  0000019aacbf1000  0000019abcbefd18  0xfffed18(268430616)
0000019abcbf0000  0000019abcbf1000  0000019accbefd68  0xfffed68(268430696)
0000019accbf0000  0000019accbf1000  0000019adcbefcf8  0xfffecf8(268430584)
0000019adcbf0000  0000019adcbf1000  0000019aecbefdc0  0xfffedc0(268430784)
0000019af0e20000  0000019af0e21000  0000019b00e1ff28  0xfffef28(268431144)
0000019b00e20000  0000019b00e21000  0000019b10047178  0xf226178(253911416)
Large object heap starts at 0x0000019a6da81000segment             begin         allocated              size
0000019a6da80000  0000019a6da81000  0000019a756d0480  0x7c4f480(130348160)
0000019b10e20000  0000019b10e21000  0000019b133ca330  0x25a9330(39490352)
Total Size:              Size: 0xf940ee70 (4181782128) bytes.
------------------------------
GC Heap Size:            Size: 0xf940ee70 (4181782128) bytes.

从最后一行可以看到堆大小: GC Heap Size: Size: 0xf940ee70 (4181782128) bytes. 然后将4181782128 byte 转化为GB: 4181782128/1024/1024/1024= 3.89G

然后再来看一下3代中有多少需要free的对象,占了多少空间,为了方便查看,大家可以用一下sosex扩展,提供了很多方便的方法。

!dumpgen xxxx 依次把0,1,2 三个代中的free空间统计出来。


0:000> !dumpgen 0 -free -statCount      Total Size      Type
-------------------------------------------------168      1,120,008   **** FREE ****168 objects, 1,120,008 bytes0:000> !dumpgen 1 -free -statCount      Total Size      Type
-------------------------------------------------368          8,096   **** FREE ****368 objects, 8,096 bytes0:000> !dumpgen 2 -free -statCount      Total Size      Type
-------------------------------------------------11,857,034  1,052,310,524   **** FREE ****11,857,034 objects, 1,052,310,524 bytes

从上面输出可以看到,三个代中需要free的信息:

对象有:168 + 368 + 11857034 = 11857570个

空间:1120008 + 8096 + 1052310524 = 1053438628 byte => 0.98G

惊讶吧~, 3.89G的堆,等待被释放的空间有0.98G,占比高达25%,再看看第2代中有高达1185万的对象需要清理,说明在整个加载过程中,GC至少被触发2次。。。

所以等GC自己启动回收不知道猴年马月,为了高效利用内存,不得已自己先给程序点个火,让程序内存降到了 3.89 - 0.98 = 2.91 G

二:对GC代机制的理解

有不少程序员对gc中的代管理机制不是特别清楚,或者看过书之后理解也停留在理论上,没法去验证书中所说,其实我也不是特别理解,????????????,作为一个准备好好玩自媒体人,不能让您白来一趟哈。

1. CLR堆模型

当CLR不小心错入程序世界的时候,会给你分配两个堆,一个叫做小对象堆,一个叫做大对象堆,默认是以83k作为大小堆的分界线,当然你也可以自定义配置,堆上的空间由很多的内存段拼成的,可能你有点蒙,我画张图吧。

2. 对临时内存段的解释

看完上图,可能大家有两个疑问:

<1> 为啥小对象堆中有一个临时内存段?

这是因为CLR做了很多假设,它假设在gen0和gen1上回收的对象会特别多,所以没事就上去转转,CLR为了方便GC快速清理回收压缩。。。就将gen0和gen1都放置在这个临时内存段上。

你可能要问,有证据吗???我就拿刚才的4G程序说话吧。


0:000> !eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x0000019b0fc66b48
generation 1 starts at 0x0000019b0f73b138
generation 2 starts at 0x0000019a5da81000
ephemeral segment allocation context: nonesegment             begin         allocated              size
0000019a5da80000  0000019a5da81000  0000019a6da7ffb8  0xfffefb8(268431288)
0000019a00000000  0000019a00001000  0000019a0ffffe90  0xfffee90(268430992)
0000019a10000000  0000019a10001000  0000019a1ffffeb0  0xfffeeb0(268431024)
0000019a20000000  0000019a20001000  0000019a2fffffb0  0xfffefb0(268431280)
0000019a30000000  0000019a30001000  0000019a3ffffc50  0xfffec50(268430416)
0000019a40000000  0000019a40001000  0000019a4fffffc8  0xfffefc8(268431304)
0000019a7aad0000  0000019a7aad1000  0000019a8aacfd60  0xfffed60(268430688)
0000019a8cbf0000  0000019a8cbf1000  0000019a9cbefe10  0xfffee10(268430864)
0000019a9cbf0000  0000019a9cbf1000  0000019aacbefcb8  0xfffecb8(268430520)
0000019aacbf0000  0000019aacbf1000  0000019abcbefd18  0xfffed18(268430616)
0000019abcbf0000  0000019abcbf1000  0000019accbefd68  0xfffed68(268430696)
0000019accbf0000  0000019accbf1000  0000019adcbefcf8  0xfffecf8(268430584)
0000019adcbf0000  0000019adcbf1000  0000019aecbefdc0  0xfffedc0(268430784)
0000019af0e20000  0000019af0e21000  0000019b00e1ff28  0xfffef28(268431144)
0000019b00e20000  0000019b00e21000  0000019b10047178  0xf226178(253911416)
Large object heap starts at 0x0000019a6da81000segment             begin         allocated              size
0000019a6da80000  0000019a6da81000  0000019a756d0480  0x7c4f480(130348160)
0000019b10e20000  0000019b10e21000  0000019b133ca330  0x25a9330(39490352)
Total Size:              Size: 0xf940ee70 (4181782128) bytes.
------------------------------
GC Heap Size:            Size: 0xf940ee70 (4181782128) bytes.

从上面gc信息中可以看到小对象堆中目前有 15个内存段, 大对象堆有2个内存段, gen0的起始地址为0x0000019b0fc66b48,gen1的起始地址为0x0000019b0f73b138, 都落在了第15个内存段内 0000019b00e20000 0000019b00e21000 0000019b10047178 0xf226178(253911416),其余内存段都被 gen2 占领,如果大家有点乱,先多看几遍,等一下看我的演示。

<2> 临时内存段大小是多少?

这个段的大小,需要看是x64还是x86机器,还要看GC是工作站模式还是服务器模式,不过msdn帮我们总结了,https://docs.microsoft.com/zh-cn/dotnet/standard/garbage-collection/fundamentals , 截个图给大家看一下。

我的本机是x64版本,工作站模式,可以通过 !eeversion 查看一下。


0:000> !eeversion
4.8.3801.0 free
Workstation mode
SOS Version: 4.8.3801.0 retail build

对应图中,我的临时内存段的最大内存是256M,再回过头用4G程序的来验证一下内存段大小,用 allocated - begin 即可。


ephemeral segment allocation context: nonesegment             begin         allocated              size
0000019b00e20000  0000019b00e21000  0000019b10047178  0xf226178(253911416)0:000> ? 0000019b10047178 - 0000019b00e21000
Evaluate expression: 253911416 = 00000000`0f226178

两者差值为 253911416 byte => 242M ,可以看出离256M不远了,等到了256M又要触发GC啦。。。。

3. 代机制简介

有了上面的基础,我觉得你对GC的gen机制应该明白了,由于3个gen运行时预定空间是随GC触发随时变动,所以就不知道某个时刻各个gen当时的空间触发阈值。

接下来说一下三代的原理:当gen0满了会触发GC回收,将gen0中活对象送到gen1中,死的就消灭掉,当某时候gen1满了,gen1的活对象会被送到gen2中,当下个某一次gen2满了,就向操作系统申请新的内存段,所以你看到了4G程序占用了多达14个内存段,就是这么一个道理,没什么复杂的。

三:代机制原理的代码演示

我刚才也说了,很多人知道这个理论,不知道怎么去验证,这里我就演示一下,先上代码:

public static void Main(string[] args){Student student1 = new Student() { UserName = "cnblogs", Email = "cnblogs@qq.com" };Student student2 = new Student() { UserName = "csdn", Email = "csdn@qq.com" };Console.WriteLine("两个对象已创建!双双进入 Gen0");Console.Read();student1 = null;GC.Collect();Console.WriteLine("Student1 已从Gen0中抹掉,助力Student2上Gen1,是否继续?");Console.ReadKey();GC.Collect();Console.WriteLine("再次助力Student2上Gen2");Console.ReadKey();Console.WriteLine("全部执行结束!");Console.ReadLine();}}public class Student{public string UserName { get; set; }public string Email { get; set; }}

代码很简单,就是想让你看一下student1和student2如何在gen0,gen1,gen2中游荡,并且给你精准找出来。

1. 探究 gen0 上的student1 和 studnet2

先启动程序,抓一下dump文件。

0:000> !clrstack -lConsoleApp4.Program.Main(System.String[]) [C:\dream\Csharp\ConsoleApp1\ConsoleApp4\Program.cs @ 18]LOCALS:0x000000017d7feeb8 = 0x000001d0962c2f280x000000017d7feeb0 = 0x000001d0962c2f480:000> !eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x000001d0962c1030
generation 1 starts at 0x000001d0962c1018
generation 2 starts at 0x000001d0962c1000
ephemeral segment allocation context: nonesegment             begin         allocated              size
000001d0962c0000  000001d0962c1000  000001d0962c7fe8  0x6fe8(28648)
Large object heap starts at 0x000001d0a62c1000segment             begin         allocated              size
000001d0a62c0000  000001d0a62c1000  000001d0a62c9a68  0x8a68(35432)
Total Size:              Size: 0xfa50 (64080) bytes.
------------------------------
GC Heap Size:            Size: 0xfa50 (64080) bytes.

仔细看上面的输出,从主线程的堆栈上可以看到student1和studnet2的地址依次为0x000001d0962c2f28, 0x000001d0962c2f48,而gen0的起始地址为:0x000001d0962c1030,刚好落在 gen0 的区间内,可能你有点蒙,我画一张图。

2. 探究 student1 被消灭,student2进入gen1

按下Enter键,执行后续代码将student1=null,再执行GC操作,看下堆中又是如何?


0:000> !clrstack -l
ConsoleApp4.Program.Main(System.String[]) [C:\dream\Csharp\ConsoleApp1\ConsoleApp4\Program.cs @ 24]LOCALS:0x000000607e9fea50 = 0x00000000000000000x000000607e9fea48 = 0x0000017f0dff2f38000000607e9fec88 00007ff8e9396c93 [GCFrame: 000000607e9fec88]
0:000> !eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x0000017f0dff6ea0
generation 1 starts at 0x0000017f0dff1018
generation 2 starts at 0x0000017f0dff1000
ephemeral segment allocation context: nonesegment             begin         allocated              size
0000017f0dff0000  0000017f0dff1000  0000017f0dff8eb8  0x7eb8(32440)
Large object heap starts at 0x0000017f1dff1000segment             begin         allocated              size
0000017f1dff0000  0000017f1dff1000  0000017f1dff9a68  0x8a68(35432)
Total Size:              Size: 0x10920 (67872) bytes.
------------------------------
GC Heap Size:            Size: 0x10920 (67872) bytes.

如果弄明白了上一个案例,看这里就很简单了,很清楚的看到studnet2落在了gen1区间段,不过从起始地址上看,gen1的空间变大了。。。我继续画一张图。

3. 探究student2 送上了 gen2


0:000> !clrstack -l
ConsoleApp4.Program.Main(System.String[]) [C:\dream\Csharp\ConsoleApp1\ConsoleApp4\Program.cs @ 28]LOCALS:0x000000d340bfebb0 = 0x00000000000000000x000000d340bfeba8 = 0x00000217b5df2f38000000d340bfede8 00007ff8e9396c93 [GCFrame: 000000d340bfede8]
0:000> !eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x00000217b5df6f40
generation 1 starts at 0x00000217b5df6ea0
generation 2 starts at 0x00000217b5df1000
ephemeral segment allocation context: nonesegment             begin         allocated              size
00000217b5df0000  00000217b5df1000  00000217b5df8f58  0x7f58(32600)
Large object heap starts at 0x00000217c5df1000segment             begin         allocated              size
00000217c5df0000  00000217c5df1000  00000217c5df9a68  0x8a68(35432)
Total Size:              Size: 0x109c0 (68032) bytes.
------------------------------
GC Heap Size:            Size: 0x109c0 (68032) bytes.

很简单,我就不画图了哈,student2的内存地址可是落在 gen2上哦~????????????

四:总结

GC.Collect尽量少用,省的把内部的分配和回收算法搞乱了,非要用的话也要理解之后再根据自己的场景使用哈。

本篇就说到这里,希望对你有帮助

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/310114.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mayor's posters POJ - 2528 (离散化+线段树)

题意&#xff1a; 在1~10000000这个区间中读取n个海报的区间信息&#xff0c;后面的海报会覆 盖前面的海报&#xff0c;问最后能看到几张海报.&#xff08;本题是一道bug题下面会提&#xff09; 题目&#xff1a; The citizens of Bytetown, AB, could not stand that the c…

[PAT乙级]1017 A除以B

本题要求计算 A/B&#xff0c;其中 A 是不超过 1000 位的正整数&#xff0c;B 是 1 位正整数。你需要输出商数 Q 和余数 R&#xff0c;使得 ABQR 成立。 输入格式&#xff1a; 输入在一行中依次给出 A 和 B&#xff0c;中间以 1 空格分隔。 输出格式&#xff1a; 在一行中依次…

服务器程序的Xamarin-Java.Interop体验(一)

这几天需要写一个用到Java模块的程序&#xff0c;但是Java是不可能写的&#xff0c;这辈子都不可能写的&#xff0c;只能搞搞interop了。目前市面上已有的基本上是IKVM.NET和JNBridgePro&#xff0c;后者没太了解技术细节&#xff0c;前者看起来是只有单向的互操作&#xff08;…

A Walk Through the Forest HDU - 1142(dijkstra+动态规划)

题目大意&#xff1a; 给你一个图&#xff0c;找最短路。但是有个非一般的的条件&#xff1a;如果a,b之间有路&#xff0c; 且你选择要走这条路&#xff0c;那么必须保证a到终点的所有路都小于b到终点的 一条路。问满足这样的路径条数 有多少。。。 解题思路 &#xff1a; 1.…

[PAT乙级]1018 锤子剪刀布

大家应该都会玩“锤子剪刀布”的游戏&#xff1a;两人同时给出手势&#xff0c;胜负规则如图所示&#xff1a; 现给出两人的交锋记录&#xff0c;请统计双方的胜、平、负次数&#xff0c;并且给出双方分别出什么手势的胜算最大。 输入格式&#xff1a; 输入第 1 行给出正整数 …

java 开源控件_一些好用的开源控件

工作两年&#xff0c;一直都在做些编码方面的表面功夫&#xff0c;实现了很多很炫的功能&#xff0c;在此写下一些体验。有些比较小的dll文件我会发上来&#xff0c;如果是开源组织的代码我会把地址附上&#xff0c;毕竟人家是会更新的。大家还有什么好用的开源控件欢迎补充。一…

揭秘 .NET 5 和Java 互操作

早早的.NET团队就立下了.NET和Java互操作的flag&#xff0c;如果你去翻一翻dotnet/runtime库&#xff0c;丝毫看不出来仓库内在搞支持。xamarin/java.interop库一直有Mono和Java互操作的实现&#xff0c;那么100%的实现.NET和Java互操作就是它&#xff0c;这两篇文章就是和你一…

[PAT乙级]1019 数字黑洞

给定任一个各位数字不完全相同的 4 位正整数&#xff0c;如果我们先把 4 个数字按非递增排序&#xff0c;再按非递减排序&#xff0c;然后用第 1 个数字减第 2 个数字&#xff0c;将得到一个新的数字。一直重复这样做&#xff0c;我们很快会停在有“数字黑洞”之称的 6174&…

Ice_cream's world I HDU - 2120(并查集判环)

题意&#xff1a;问给出的望塔之间的建造了围墙&#xff0c;将土地分成了几份 思路&#xff1a;用并查集判环&#xff0c;若有围墙相接的瞭望塔&#xff0c;有相同的父根&#xff0c;则存在环 ice_creams world is a rich country, it has many fertile lands. Today, the qu…

java 普通类获取session_如何在Java的普通类中获取Session以及request对象

展开全部你是指你的 web 项目中使用到一个工具性的类&#xff0c;它的e69da5e6ba9062616964757a686964616f31333337613865形参中没有 HttpRequest 或 HttpSession 参数&#xff1f;如果是这样的话&#xff0c;我们需要使用一个 ThreadLocal 变量&#xff0c;我们把当前 reques…

[PAT乙级]1020 月饼

月饼是中国人在中秋佳节时吃的一种传统食品&#xff0c;不同地区有许多不同风味的月饼。现给定所有种类月饼的库存量、总售价、以及市场的最大需求量&#xff0c;请你计算可以获得的最大收益是多少。 注意&#xff1a;销售时允许取出一部分库存。样例给出的情形是这样的&#…

java中的asList_Java Arrays.AsList原理及用法实例

java.util.Arrays的asList方法可以方便的将数组转化为集合&#xff0c;我们平时开发在初始化ArrayList时使用的比较多&#xff0c;可以简化代码&#xff0c;但这个静态方法asList()有几个坑需要注意:一. 如果对集合使用增加或删除元素的操作将会报错如下代码&#xff1a;List l…

为什么顶尖高手,都是长期主义者?

点击蓝字关注&#xff0c;回复“职场进阶”获取职场进阶精品资料一份在当下这个浮躁的社会&#xff0c;很多人都认为“快”才是真理&#xff0c;“慢”就是原罪。大家都对一夜腾飞的故事津津乐道&#xff0c;却觉得坚守一生最终获得大成的故事不够性感。但事实是怎么样的呢&…

Invitation Cards POJ - 1511 SPFA(dijkstra+反向建图+邻接表(下标过大)+输入输出用stdio(iostream超时))

题目大意&#xff1a; 有编号1&#xff5e;P的站点&#xff0c; 有Q条公交车路线&#xff0c;公交车路线只从一个起点站直接 到达终点站&#xff0c;是单向的&#xff0c;每条路线有它自己的车费。有P个人早上从1出发 &#xff0c;他们要到达每一个公交站点&#xff0c; 然后到…

Asp.Net Boilerplate微服务实战(一)概述

ABP目前分为两个主版本&#xff1a;原版ABP框架&#xff0c;以及新版本也就是大家通常说的AbpvNext&#xff0c;官方后将ABP原版本框架命名为Asp.Net Boilerplate&#xff0c;新版本ABPvNext称为ABP框架。为方便阅读&#xff0c;本系列将原版本ABP框架统一称为Asp.Net Boilerpl…

[PAT乙级]1022 D进制的A+B

输入两个非负 10 进制整数 A 和 B (≤2​30​​−1)&#xff0c;输出 AB 的 D (1<D≤10)进制数。 输入格式&#xff1a; 输入在一行中依次给出 3 个整数 A、B 和 D。 输出格式&#xff1a; 输出 AB 的 D 进制数。 输入样例&#xff1a; 123 456 8输出样例&#xff1a; 1…

Tree Recovery(二叉树递归遍历+求后序遍历模板)

题意&#xff1a;已知先序和中序&#xff0c;将后序求出来 Little Valentine liked playing with binary trees very much. Her favorite game was constructing randomly looking binary trees with capital letters in the nodes. This is an example of one of her creati…

java中函数是什么_[一] java8 函数式编程入门 什么是函数式编程 函数接口概念 流和收集器基本概念...

本文是针对于java8引入函数式编程概念以及stream流相关的一些简单介绍什么是函数式编程?java程序员第一反应可能会理解成类的成员方法一类的东西此处并不是这个含义,更接近是数学上的函数看一下百度百科中关于函数的说明函数的定义&#xff1a;给定一个数集A&#xff0c;假设其…

分布式系统不得不说的CAP定理

21天学会C语言&#xff1f;3天学会弹钢琴&#xff1f;放弃一切错误方法&#xff0c;从今天开始“刻意练习”&#xff0c;因为这才是最强大的&#xff0c;也是唯一正确的学习方法。--《刻意练习》Anders Ericsson引言CAP问题已经成了计算机科学中一个研究领域&#xff0c;之前说…

[PAT乙级]1021 个位数统计

输入格式&#xff1a; 每个输入包含 1 个测试用例&#xff0c;即一个不超过 1000 位的正整数 N。 输出格式&#xff1a; 对 N 中每一种不同的个位数字&#xff0c;以 D:M 的格式在一行中输出该位数字 D 及其在 N 中出现的次数 M。要求按 D 的升序输出。 输入样例&#xff1a;…