电话+V:159999-78052,欢迎咨询sql数据库内存泄漏怎么解决呢,[小程序设计与开发],[小程序投流与推广],[小程序后台搭建],[小程序整套源码打包],[为个体及小微企业助力],[电商新零售模式],[小程序运营推广及维护]
经常使用MSSQL的朋友都会发现一个小小的网站在运行若干天后MSSQL就会把服务器上所有的内存都吃光,此时你不得不重新启动一下服务器或mssql来释放内存,有人认为是MSSQL有内存泄露问题,其实不然,微软给我们了明确说明:在您启动SQLServer之后,SQLServer内存使用量将会持续稳定上升,即使当服务器上活动很少时也不会下降。另外,任务管理器和性能监视器将显示计算机上可用的物理内存稳定下降,直到可用内存降到4至10MB为止。
仅仅出现这种状态不表示内存泄漏。此行为是正常的,并且是SQLServer缓冲池的预期行为。
默认情况下,SQLServer根据操作系统报告的物理内存加载动态增大和收缩其缓冲池(缓存)的大小。只要有足够的内存可用于防止内存页面交换(在4至10MB之间),SQLServer缓冲池就会继续增大。像在与SQLServer分配内存位于相同计算机上的其他进程一样,SQLServer缓冲区管理器将在需要的时候释放内存。SQLServer每秒可以释放和获取几兆字节的内存,从而使它可以快速适应内存分配变化。
更多信息
您可以通过服务器内存最小值和服务器内存最大值配置选项设置SQLServer数据库引擎使用的内存(缓冲池)量的上下限。在设置服务器内存最小值和服务器内存最大值选项之前,请查阅以下Microsoft知识库文章中标题为'内存'一节中的参考信息:319942HOWTO:DeterminePropersqlserver(WINDOWS平台上强大的数据库平台)ConfigurationSettings(确定正确的sqlserver(WINDOWS平台上强大的数据库平台)配置设置)
请注意,服务器内存最大值选项只限制SQLServer缓冲池的大小。服务器内存最大值选项不限制剩余的未保留内存区域,sqlserver(WINDOWS平台上强大的数据库平台)准备将该区域分配给其他组件,例如扩展存储过程、COM对象、以及非共享DLL、EXE和MAPI组件。由于前面的分配SQLServer专用字节超过服务器内存最大值配置是很正常的。有关此未保留内存区域中分配的其他信息,请单击下面的文章编号,以查看Microsoft知识库中相应的文章:316749PRB:在使用大量数据库时可能没有足够的虚拟内存
下面我们就来实战如何限制MSSQL内存使用:
第一步:打开企业管理器双击进入要修改的MSSQL.
第二步:在左侧MSSQL上点击右键,选择属性,弹出SQLServer属性(配置)对话框(最好打上SQLSP4补丁)
第三步:点击内存选项卡.在这里,你会看到MSSQL默认设置为使用最大内存,也就是你所有的内存,根据你的需要,设置它的最大值(一般为物理内存-128M)和最小值(一般为最大内存的1/4)吧.
数据库服务器负载过高,数据库连接超时,内存泄漏。
1、数据库服务器负载过高:如果数据库服务器负载过高,会导致SQL编辑器无法完成数据读取和处理任务,从而引起程序崩溃或者重启。为了避免这种情况发生,可以考虑优化查询语句,或者增加硬件配置以提高服务器性能。
2、数据库连接超时:如果SQL编辑器连接数据库的时间过长,会触发超时机制而导致程序重启。为了避免连接超时,可以尝试通过优化网络配置或者调整SQL编辑器的连接参数来缩短连接时间。
github地址
github.com/whx123/Java…一、问题描述上周五晚上主营出现部分设备掉线,经过查看日志发现是由于缓存系统出现长时间gc导致的。这里的gc日志的特点是:
然后紧急把剩余未gc的一个节点内存dump下来,使用mat工具打开发现,
com.mysql.jdbc.NonRegisteringDriver对象占了堆内存的大部分空间。
查看对象数量,发现
com.mysql.jdbc.NonRegisteringDriver$
ConnectionPhantomReference这个对象堆积了10140个。
初步判断长时间gc的问题应该是由于
com.mysql.jdbc.NonRegisteringDriver$
ConnectionPhantomReference这个对象大量堆积引起的。
目前正式环境使用数据库相关依赖如下:
依赖版本
mysql5.1.47hikari2.7.9Sharding-jdbc3.1.0根据以上描述,提出以下问题:
简单来说,NonRegisteringDriver类有个虚引用集合connectionPhantomRefs用于存储所有的数据库连接,
NonRegisteringDriver.trackConnection方法负责把新创建的连接放入connectionPhantomRefs集合。源码如下:
我们追踪创建数据库连接的过程源码,发现其中会调到
com.mysql.jdbc.ConnectionImpl的构造函数,该方法会调用createNewIO方法创建一个新的数据库连接MysqlIO对象,然后调用我们上面提到的
NonRegisteringDriver.trackConnection方法,把该对象放入
NonRegisteringDriver.connectionPhantomRefs集合。源码如下:
connectionPhantomRefs是一个虚引用集合,何为虚引用?为什么设计为虚引用队列
虚引用队列也称为“幽灵引用”,它是最弱的一种引用关系。
如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。
为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。
当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在垃圾回收后,将这个虚引用加入引用队列,在其关联的虚引用出队前,不会彻底销毁该对象。所以可以通过检查引用队列中是否有相应的虚引用来判断对象是否已经被回收了。
connectionPhantomRefs这种对象为什么会大量堆积,JVM回收不过来了?这里结合项目中hikaricp数据配置和官方文档结合说明~
我们先查阅hikaricp数据池的官网地址,看看部分属性介绍如下:
maximumPoolSizeThispropertycontrolsthemaximumsizethatthepoolisallowedtoreach,includingbothidleandin-useconnections.Basicallythisvaluewilldeterminethemaximumnumberofactualconnectionstothedatabasebackend.Areasonablevalueforthisisbestdeterminedbyyourexecutionenvironment.Whenthepoolreachesthissize,andnoidleconnectionsareavailable,callstogetConnection()willblockforuptoconnectionTimeoutmillisecondsbeforetimingout.Pleasereadaboutpoolsizing.Default:10maximumPoolSize控制最大连接数,默认为10
minimumIdleThispropertycontrolstheminimumnumberofidleconnectionsthatHikariCPtriestomaintaininthepool.IftheidleconnectionsdipbelowthisvalueandtotalconnectionsinthepoolarelessthanmaximumPoolSize,HikariCPwillmakeabestefforttoaddadditionalconnectionsquicklyandefficiently.However,formaximumperformanceandresponsivenesstospikedemands,werecommendnotsettingthisvalueandinsteadallowingHikariCPtoactasafixedsizeconnectionpool.Default:sameasmaximumPoolSizeminimumIdle控制最小连接数,默认等同于maximumPoolSize,10。
?idleTimeoutThispropertycontrolsthemaximumamountoftimethataconnectionisallowedtositidleinthepool.ThissettingonlyapplieswhenminimumIdleisdefinedtobelessthanmaximumPoolSize.IdleconnectionswillnotberetiredoncethepoolreachesminimumIdleconnections.Whetheraconnectionisretiredasidleornotissubjecttoamaximumvariationof+30seconds,andaveragevariationof+15seconds.Aconnectionwillneverberetiredasidlebeforethistimeout.Avalueof0meansthatidleconnectionsareneverremovedfromthepool.Theminimumallowedvalueis10000ms(10seconds).Default:600000(10minutes)连接空闲时间超过idleTimeout(默认10分钟)后,连接会被抛弃
?maxLifetimeThispropertycontrolsthemaximumlifetimeofaconnectioninthepool.Anin-useconnectionwillneverberetired,onlywhenitisclosedwillitthenberemoved.Onaconnection-by-connectionbasis,minornegativeattenuationisappliedtoavoidmass-extinctioninthepool.Westronglyrecommendsettingthisvalue,anditshouldbeseveralsecondsshorterthananydatabaseorinfrastructureimposedconnectiontimelimit.Avalueof0indicatesnomaximumlifetime(infinitelifetime),subjectofcoursetotheidleTimeoutsetting.Default:1800000(30minutes)连接生存时间超过maxLifetime(默认30分钟)后,连接会被抛弃.
我们再回头看看项目的hikari配置:
猜测问题产生的根源:
每次新建一个数据库连接,都会把该连接放入connectionPhantomRefs集合中。数据连接在空闲时间超过idleTimeout或生存时间超过maxLifetime后会被废弃,在connectionPhantomRefs集合中等待回收。因为连接资源一般存活时间比较久,经过多次YoungGC,一般都能存活到老年代。如果这个数据库连接对象本身在老年代,connectionPhantomRefs中的元素就会一直堆积,直到下次fullgc。如果等到fullgc的时候connectionPhantomRefs集合的元素非常多,该次fullgc就会非常耗时。
那么怎么解决呢?可以考虑优化minimumIdle、maximumPoolSize、idleTimeout、maxLifetime这些参数,下一小节我们分析一波
三、问题验证线上模拟环境为了验证问题,我们需要模拟线上环境,调整maxLifetime等参数~压测思路如下:
这里有以下注意点:
最终环境配置如下:
模拟实验结果确认hikari和jvm配置生效
观察jvisualvm,发现产生20个NonRegisteringDriver对象
观察hikari日志,确认有20个连接对象生成,以及产生总连接10个,空闲连接10个。
初步判断一个数据库连接会生成两个NonRegisteringDriver对象。
启动压测程序,压测1000s期间观察gc日志,gc时间间隔约20s,100s后发生5次gc
观察hikari日志,确认有20个连接对象生成
观察jvisualvm变成40个NonRegisteringDriver对象,符合预期。
持续观察,1000s后理论上会产生220个对象(20+20*1000s/100s),查看jvisualvm如下
产生了240个对象,基本和预期符合。
实验结果分析再结合我们生产的问题,假设我们每天14个小时高峰期(12:00~凌晨2:00),期间连接数20,10个小时低峰期,期间连接数10,每次fullgc间隔14天,等到下次fullgc堆积的NonRegisteringDriver对象为(20*14+10*10)*2*14=10640,与问题dump里面NonRegisteringDriver对象的数量10140个基本吻合。
至此问题根源已经得到完全确认!!!
四、问题解决方案由上面分析可知,问题产生的废弃的数据库连接对象堆积,最终导致fullgc时间过长。所以我们可以从以下方面思考解决方案:
我们可以考虑设置maxLifetime为一个较大的值,用于延长连接的生命周期,减少产生被废弃的数据库连接的频率,等到下次fullgc的时候需要清理的数据库连接对象会大大减少。
Hikari推荐maxLifetime设置为比数据库的wait_timeout时间少30s到1min。如果你使用的是mysql数据库,可以使用showglobalvariableslike'%timeout%';查看wait_timeout,默认为8小时。
下面开始验证,设置maxLifetime=1小时,其他条件不变。压测启动前观察jvisualvm,NonRegisteringDriver对象数量为20
1000s,观察NonRegisteringDriver对象仍然为20
NonRegisteringDriver对象没有发生堆积,问题得到解决。
同时另外注意:minimumIdle和maximumPoolSize不要设置得太大,一般来说配置minimumIdle=10,maximumPoolSize=10~20即可。
【使用G1回收器】G1回收器是目前java垃圾回收器的最新成果,是一款低延迟高吞吐的优秀回收器,用户可以自定义最大暂停时间目标,G1会尽可能在达到高吞吐量同时满足垃圾收集暂停时间目标。
下面开始验证G1回收器的实用性,该验证过程需要一段较长时间的观察,同时借助链路追踪工具skywalking。最终观察了10天,结果图如下:使用G1回收器,部分jvm参数-Xms3G-Xmx3G-XX:+UseG1GC
使用java8默认的ParallelGC回收器组合,部分jvm参数-Xms3G-Xmx3G
以上图中四个内容,从左到右分别为
我们可以看到使用ParallelGC回收器组合的服务消耗的内存速度较快,发生了6996次younggc且发生了一次fullgc,fullgc时间长达5s。另外一组使用G1回收器的服务消耗内存速度较为平稳,只发生3827次younggc且没有发生fullgc。由此可以看到G1回收器确实可以用来解决我们的数据库连接对象堆积问题。
【建立巡查系统】这个我们目前还没有经过实践,但是根据上面分析结果判断,定期触发fullgc可以达到每次清理少量堆积的数据库连接的作用,避免过多数据库连接一直堆积。采用该方法需要对业务的内容和高低峰周期非常熟悉。实现思路参考如下:
我们这次问题产生的根源是数据库连接对象堆积,导致fullgc时间过长。解决思路可以从以下三点入手:
电话+V: 159999-78052
专注于小程序推广配套流程服务方案。为企业及个人客户提供了高性价比的运营方案,解决小微企业和个体拓展客户的问题