热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

记一次go服务内存异常增涨

这几天发现服务的内存一直往上涨,这是监控看到的图标,可以一眼看出 最后一段线变平了是因为业务方的调用停掉了。  遇到这种情况,首先想到的是查看内存分布图,于是调用pprof,拿到内

这几天发现服务的内存一直往上涨,这是监控看到的图标,可以一眼看出

 

最后一段线变平了是因为业务方的调用停掉了。

记一次go服务内存异常增涨

 

 

遇到这种情况,首先想到的是查看内存分布图,于是调用pprof,拿到内存分布图

记一次go服务内存异常增涨

 

 

 

我们的机器是16G的,从监控图表看到内存占用达到了百分之50多,将近10个G,但是pprof那边显示内存占用只有2.58G,而且可以清楚的看到,其中两个G是copy函数生成的,0.5个G是加载字典文件的内存,从图上面看,内存分布完全符合我们的预期。但是机器上显示占用了10个G,那多余的7个多G去哪了?

为了搞明白这个,于是打印了GC的信息

{"level":"debug","msg":"GC-0:sys=10128MB HeapAlloc=5028MB HeapSys=9661MB","time":"2020-07-24 06:45:30.549"} {"level":"debug","msg":"GC-1:sys=10128MB HeapAlloc=2659MB HeapSys=9661MB","time":"2020-07-24 06:45:31.135"} {"level":"debug","msg":"GC-0:sys=10128MB HeapAlloc=4702MB HeapSys=9661MB","time":"2020-07-24 07:15:07.134"} {"level":"debug","msg":"GC-1:sys=10128MB HeapAlloc=2639MB HeapSys=9661MB","time":"2020-07-24 07:15:07.733"} {"level":"debug","msg":"GC-0:sys=10128MB HeapAlloc=5048MB HeapSys=9661MB","time":"2020-07-24 07:30:45.724"} {"level":"debug","msg":"GC-1:sys=10128MB HeapAlloc=2650MB HeapSys=9661MB","time":"2020-07-24 07:30:46.327"} {"level":"debug","msg":"GC-0:sys=10128MB HeapAlloc=4985MB HeapSys=9661MB","time":"2020-07-24 07:45:28.112"} {"level":"debug","msg":"GC-1:sys=10128MB HeapAlloc=2663MB HeapSys=9661MB","time":"2020-07-24 07:45:28.672"} {"level":"debug","msg":"GC-0:sys=10128MB HeapAlloc=4725MB HeapSys=9661MB","time":"2020-07-24 08:15:07.868"} {"level":"debug","msg":"GC-1:sys=10128MB HeapAlloc=2650MB HeapSys=9661MB","time":"2020-07-24 08:15:08.444"}

 

这是正式服的数据,GC-0是触发垃圾回收前的数据,GC-1是完成垃圾回收后的数据。可以看到 HeapSys用了9个多G,sys10个G,HeapAlloc则是2.5个G和5个G

5个G是垃圾回收之前打印的信息,而2.5个G是垃圾回收之后打印的信息,可以看到,垃圾回收之后HeapAlloc内存明显减小了。

但是为什么另外两个指标没减下去呢?

我在测试服加了些指标打印,如下:

{"level":"info","msg":"GC-0:sys=6109MB HeapAlloc=4856MB HeapSys=5822MB HeapIdle=495MB HeapInuse=5327MB HeapReleased=0MB HeapObjects=24977750","time":"2020-07-22 16:36:52.159"}

{"level":"info","msg":"GC-1:sys=6112MB HeapAlloc=2673MB HeapSys=5822MB HeapIdle=2680MB HeapInuse=3142MB HeapReleased=0MB HeapObjects=16438129","time":"2020-07-22 16:36:52.883"}

首先搞清楚每个指标的含义:

sys是程序所占用的堆空间+栈空间(包含虚拟空间)

HeapSys是程序占用 的堆空间(包含虚拟空间)

HeapAlloc是程序对象实际使用的堆空间,即span里面的object被分配出去的空间

HeapIdle是空闲的堆空间(包含虚拟空间)

HeapReleased是被释放的堆空间

HeapInuse是在使用的堆空间(只要span里面的一个object被使用了,整个span都被计入其中。所以这个比HeapAlloc大)

 

结合前面的pprof,可以看到HeapAlloc基本与pprof图标看到的内存消耗吻合,而且一直维持在2.5g左右,说明我们程序内存还是挺正常的,并没有产生内存泄漏。

结合go的内存分配策略,go在申请内存的时候,会预先申请一块大内存以备用。比如我程序需要用1个G的内存,他有可能会申请1.5个G,而且当垃圾回收后,程序内存释放,也只是归还到mspan,mcentral和mheap。并不会归还给操作系统。当正真归还给操作系统,大概在5分钟之后,前提是内存过多。

 

显然10个G的内存太多了,最终也没归还给系统。具体问题就变成了HeapSys的空闲内存为啥没返回给操作系统。而HeapSys = HeapInuse + HeapIdle,所以就是HeapIdle(空闲的堆空间)为啥没把内存返还给操作系统。

 

这边于是做了一个实验,就写了个程序去抢占内存。最终发现当内存不够用的时候,go会释放多余的空间。HeapReleased=2657MB,显示释放了2657MB空间(测试环境)。

但是在打印的堆信息上面,HeapIdle还是没有减小。这是因为那边显示的是虚拟空间,实际占用的空间已经还回去了。

进一步发现,HeapReleased是HeapIdle的子集,HeapIdle-HeapReleased 就是程序实际可用的空闲空间(未分配的span)。

 

最终通过百度发现,go在1.12版本的时候修改了他的内存回收策略,变成了惰性回收。

当系统内存不够的时候,go才会把这部分内存归还系统。这样做的好处是go向系统申请空间的时候,可以复用之前未被回收的堆内存,而不会触发缺页异常,从而导致内存重新分配。

 

我们可以通过参数GODEBUG=madvdOntneed=1 回退会1.11版本的回收策略。

即GODEBUG=madvdOntneed=1 ./server

 


推荐阅读
  • 手把手教你使用GraphPad Prism和Excel绘制回归分析结果的森林图
    本文介绍了使用GraphPad Prism和Excel绘制回归分析结果的森林图的方法。通过展示森林图,可以更加直观地将回归分析结果可视化。GraphPad Prism是一款专门为医学专业人士设计的绘图软件,同时也兼顾统计分析的功能,操作便捷,可以帮助科研人员轻松绘制出高质量的专业图形。文章以一篇发表在JACC杂志上的研究为例,利用其中的多因素回归分析结果来绘制森林图。通过本文的指导,读者可以学会如何使用GraphPad Prism和Excel绘制回归分析结果的森林图。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • Nginx使用(server参数配置)
    本文介绍了Nginx的使用,重点讲解了server参数配置,包括端口号、主机名、根目录等内容。同时,还介绍了Nginx的反向代理功能。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • 解决Cydia数据库错误:could not open file /var/lib/dpkg/status 的方法
    本文介绍了解决iOS系统中Cydia数据库错误的方法。通过使用苹果电脑上的Impactor工具和NewTerm软件,以及ifunbox工具和终端命令,可以解决该问题。具体步骤包括下载所需工具、连接手机到电脑、安装NewTerm、下载ifunbox并注册Dropbox账号、下载并解压lib.zip文件、将lib文件夹拖入Books文件夹中,并将lib文件夹拷贝到/var/目录下。以上方法适用于已经越狱且出现Cydia数据库错误的iPhone手机。 ... [详细]
  • 利用Visual Basic开发SAP接口程序初探的方法与原理
    本文介绍了利用Visual Basic开发SAP接口程序的方法与原理,以及SAP R/3系统的特点和二次开发平台ABAP的使用。通过程序接口自动读取SAP R/3的数据表或视图,在外部进行处理和利用水晶报表等工具生成符合中国人习惯的报表样式。具体介绍了RFC调用的原理和模型,并强调本文主要不讨论SAP R/3函数的开发,而是针对使用SAP的公司的非ABAP开发人员提供了初步的接口程序开发指导。 ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • 树莓派语音控制的配置方法和步骤
    本文介绍了在树莓派上实现语音控制的配置方法和步骤。首先感谢博主Eoman的帮助,文章参考了他的内容。树莓派的配置需要通过sudo raspi-config进行,然后使用Eoman的控制方法,即安装wiringPi库并编写控制引脚的脚本。具体的安装步骤和脚本编写方法在文章中详细介绍。 ... [详细]
  • 一次上线事故,30岁+的程序员踩坑经验之谈
    本文主要介绍了一位30岁+的程序员在一次上线事故中踩坑的经验之谈。文章提到了在双十一活动期间,作为一个在线医疗项目,他们进行了优惠折扣活动的升级改造。然而,在上线前的最后一天,由于大量数据请求,导致部分接口出现问题。作者通过部署两台opentsdb来解决问题,但读数据的opentsdb仍然经常假死。作者只能查询最近24小时的数据。这次事故给他带来了很多教训和经验。 ... [详细]
  • Netty源代码分析服务器端启动ServerBootstrap初始化
    本文主要分析了Netty源代码中服务器端启动的过程,包括ServerBootstrap的初始化和相关参数的设置。通过分析NioEventLoopGroup、NioServerSocketChannel、ChannelOption.SO_BACKLOG等关键组件和选项的作用,深入理解Netty服务器端的启动过程。同时,还介绍了LoggingHandler的作用和使用方法,帮助读者更好地理解Netty源代码。 ... [详细]
  • CentOS7.8下编译muduo库找不到Boost库报错的解决方法
    本文介绍了在CentOS7.8下编译muduo库时出现找不到Boost库报错的问题,并提供了解决方法。文章详细介绍了从Github上下载muduo和muduo-tutorial源代码的步骤,并指导如何编译muduo库。最后,作者提供了陈硕老师的Github链接和muduo库的简介。 ... [详细]
  • Python脚本编写创建输出数据库并添加模型和场数据的方法
    本文介绍了使用Python脚本编写创建输出数据库并添加模型数据和场数据的方法。首先导入相应模块,然后创建输出数据库并添加材料属性、截面、部件实例、分析步和帧、节点和单元等对象。接着向输出数据库中添加场数据和历程数据,本例中只添加了节点位移。最后保存数据库文件并关闭文件。文章还提供了部分代码和Abaqus操作步骤。另外,作者还建立了关于Abaqus的学习交流群,欢迎加入并提问。 ... [详细]
  • 生产环境下JVM调优参数的设置实例
     正文前先来一波福利推荐: 福利一:百万年薪架构师视频,该视频可以学到很多东西,是本人花钱买的VIP课程,学习消化了一年,为了支持一下女朋友公众号也方便大家学习,共享给大家。福利二 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
author-avatar
海淀区小张_703
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有