内存迟迟下不去,可能你就差一个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乙级]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;这两篇文章就是和你一…

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

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

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

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

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;…

Largest Rectangle in a Histogram (动态规划+奇思妙想单调栈)求最大矩状图面积

感觉动态规划都是玄妙的很&#xff0c;思维题吧&#xff08;单调栈思维&#xff09; 题解&#xff1a;让求最大矩形面积&#xff0c;宽为1&#xff0c;暴力超时 可以发现 当第i-1个比第i个高的时候 比第i-1个高的所有也一定比第i个高 于是可以用到动态规划的思想 令l…

ASP.NET Core分布式项目实战(详解oauth2授权码流程)--学习笔记

最近公司产品上线&#xff0c;通宵加班了一个月&#xff0c;一直没有更新&#xff0c;今天开始恢复&#xff0c;每日一更&#xff0c;冲冲冲任务13&#xff1a;详解oauth2授权码流程我们即将开发的产品有一个用户 API&#xff0c;一个项目服务 API&#xff0c;每个服务都需要认…

C++类模板中的static成员

从类模板实例化的每一个模板类有自己的类模板数据成员&#xff0c;该模板的所有对象共享一个static数据成员。 代码如下: #include <iostream> using namespace std;template<typename T> class Person { public:static int a;};template<typename T> int …

C#并发编程之初识并行编程

写在前面之前微信公众号里有一位叫sara的朋友建议我写一下Parallel的相关内容&#xff0c;因为手中商城的重构工作量较大&#xff0c;一时之间无法抽出时间。近日&#xff0c;这套系统已有阶段性成果&#xff0c;所以准备写一下Parallel的相关内容&#xff0c;正好也延续之前的…

java 下拉列表 枚举_「Java三分钟」精准而优雅——枚举类详解

关注我&#xff0c;每天三分钟&#xff0c;带你轻松掌握一个Java相关知识点。1.为什么要用枚举你在读一个老工程代码时&#xff0c;是否经常看见有几个类&#xff0c;里面放着成百上千的静态常量&#xff0c;场面相当恐怖&#xff0c;而且如果不加注释&#xff0c;很多你都不知…

Not so Mobile(二叉树递归输入同时建树){天平}

题意&#xff1a; 给出一个大天平&#xff0c;大天平中还有许多小天平&#xff0c;求出所有的天平是否平衡&#xff1b;平衡条件为wldl wrdr&#xff1b; 题目 Before being an ubiquous communications gadget, a mobile was just a structure made of strings and wires …

在阿里云函数计算上部署.NET Core 3.1

使用阿里云ECS或者其他常见的VPS服务部署应用的时候&#xff0c;需要手动配置环境&#xff0c;并且监测ECS的行为&#xff0c;做补丁之类的&#xff0c;搞得有点复杂。好在很多云厂商&#xff08;阿里云、Azure等&#xff09;提供了Serverless服务&#xff0c;借助于Serverless…

java 持续交付_【Java架构:持续交付】一篇文章搞掂:Jenkins

1.1、使用yum安装JDKa、检查系统是否有安装open-jdkrpm -qa |grep javarpm -qa |grep jdkrpm -qa |grep gcj如果没有输入信息表示没有安装。如果安装可以使用rpm -qa | grep java | xargs rpm -e --nodeps 批量卸载所有带有Java的文件 这句命令的关键字是javab、检索yum中包含…

抱歉,请不要把 “业务逻辑层” 理解为 “业务中台”

这是头哥侃码的第197篇原创在IAS2019中台架构峰会上&#xff0c;我曾与一位年轻帅气的技术小伙来了一番有趣的对话。因为和朋友有约&#xff0c;所以我在现场互动结束之后&#xff0c;就急匆匆地跟其他嘉宾打了声招呼&#xff0c;抱着笔记本冲出了会场。但没想到刚到电梯口&…

C++异常处理分析

C异常处理基本语法: 代码如下: #include <iostream> using namespace std;int divide(int x, int y) {if (y 0) throw y;return x / y; }void test01() {//试着去捕获异常try{divide(10, 0);}/*catch (int){cout << "除数为0!" << endl;} */catc…

陌陌的 Service Mesh 探索与实践

Service Mesh Virtual Meetup 是 ServiceMesher 社区和 CNCF 联合主办的线上系列直播。本期为 Service Mesh Virtual Meetup#1 &#xff0c;邀请了四位来自不同公司的嘉宾&#xff0c;从不同角度展开了 Service Mesh 的应用实践分享&#xff0c;分享涵盖来自陌陌和百度的 Servi…