mingche 发表于 2018-10-12 09:22:53

SQL Server内存泄漏

  SQL Server内存泄漏
  翻译自:https://mssqlwiki.com/2012/12/04/sql-server-memory-leak/
  什么是内存泄漏?
  当一个进程分配了内存,它应该回收并释放给操作系统。如果由于代码里的缺陷没有回收内存,被称为泄漏,它会导致操作系统和应用程序的内存压力。
  有关SQL Server内存泄漏的神话
  SQL Server内存管理设计为基于系统可用内存数量和SQL Server里Max server memory设置来动态增加和收缩内存。
  很多次,系统管理员查看SQL Server内存使用,如果他们发现SQL Server内存使用很高会认为SQL Server内存泄漏了。
  这是不对的,SQL Server是基于服务的应用程序,它的内存管理器设计为按需(除了大页)增加内存使用,除非Windows发出低内存提醒,否则不会收缩它的内存使用。我们可以在SQL Server里通过配置Max server memory来控制SQL Server内存使用。这个配置限制了SQL Server Bpool的使用,而不会控制整个SQL Server内存使用。分配在BPOOL以外(也叫MTL或MTR)的SQL Server内存部分,我们没有办法控制在bpool外SQL Server可以使用多少内存,但是non-bpool内存使用通常很低,可以很容易通过研究运行在SQL Server里的组件来预估。
  例如:如果你想SQL Server在服务器上只使用10GB内存。考虑在Bpool以外SQL Server可能需要多少内存,并相应的设置“Max server memory”。在这里,如果你预估在Bpool以外SQL Server使用1.5GB,那设置Max server memory为8.5GB。
  什么可以导致SQL Server内存泄漏?
  SQL Server代码有一个逻辑去分配内存,但是不会回收它。如果在SQL Server里有任何组件导致了内存泄漏,它可以很容易使用像sys.dm_os_memory_allocation、sys.dm_os_memory_clerks和sys.dm_os_memory_objects等识别出来。但是,SQL Server里大多数的内存泄漏时由加载到SQL Server进程里的第三方DLL导致的。
  注意:加载到SQL Server里的非SQL Server DLL的所有内存分配将会产生“Mem to Leave”(在Bpool之外),并且他们被称为直接Windows分配(DWA)。
  当在SQL Server里有内存溢出条件,如果你猜测有内存泄漏。首先确定谁在消耗内存。如果SQL Server没有使用在MemToLeave里的大多数内存,并且你仍然获得Mem to Leave,有可能存在内存泄漏,它由加载到SQL Server里某个DLL导致。参考第1部分(MTL错误)https://mssqlwiki.com/sqlwiki/sql-performance/troubleshooting-sql-server-memory/
  以下查询可以用于确定SQL Server在MTL里的实际内存消耗。
select sum(multi_pages_kb)from sys.dm_os_memory_clerks  如果SQL Server的内存消耗非常低,并且你仍然看到像下面的SQL Server内存错误,请重点注意泄漏问题。
  例如:
SQL Server 2000  
                WARNING: Failed to reserve contiguous memory of Size= 65536.
  
                WARNING: Clearing procedure cache to free contiguous memory.
  
                Error: 17802 “Could not create server event thread.”
  
                SQL Server could not spawn process_loginread thread.
  
SQL Server 2005/2008
  
                Failed Virtual Allocate Bytes: FAIL_VIRTUAL_RESERVE 122880
  如何识别内存泄漏和故障排除?
  在Windows里有多种方法识别谁在进程里泄漏内存。在这边博文里,我们将讨论如何使用1.Windows debugger、2.Debug diagnostics tools for windows和3.UMDH识别内存泄漏。
  让我们创建一个示例DLL加载到SQL Server进程里泄漏内存,看看如何使用以上提到的工具排除泄漏故障。
  从这个链接(http://sdrv.ms/TH1qfR)下载HeapLeak.dll,并安装32位(http://www.microsoft.com/en-us/download/details.aspx?id=5555)或64位(http://www.microsoft.com/en-us/download/details.aspx?id=14632)的Microsoft Wisual C++ 2010 Redistributable Package来让这个DLL运行。
-- Create an extended stored procedure in SQL Server  
exec sp_addextendedproc'HeapLeak','C:\HeapLeakdll\HeapLeak.dll'
  
-- Let us execute this Extended SP 30 times and leak memory.
  
exec HeapLeak
  
go 30
  我们也将在SQL Server里启用以下跟踪标志,当有内存溢出错误时自动产生过滤dump并查看如何识别谁在泄漏内存。
-- 2551 is used to enable filter dump.  
dbcc traceon (2551,-1)
  
go
  
-- 8004 is used to take memory dump on first occurrence of OOM condition
  
dbcc traceon (8004,-1)
  
go
  
-- Note: Both the trace flags listed above are un-documented, So use it at your own risk and there is no guarantee that this trace flags will work in future versions of SQL Server
  当我们启用跟踪标志,我们在SQL Server里触发内存溢出错误而产生OOM内存dump。通过执行以上的扩展存储过程30次,我们从MTL泄漏了大约300MB内存。
  让我们执行以下创建XML句柄的脚本。XML句柄从MTL的内存分配将不久获得内存溢出错误,因为我们执行的扩展存储过程已经泄漏了内存。
  (不要在没有执行HeapLeak就直接运行以下XML脚本。以下导致OOM错误的脚本由于对每个执行创建的句柄,但是它占用SQL Server分配,所以不会帮助我们来理解如何排除由第三方DLL泄漏导致的故障)
  注意:
  1. SQL Server内存dump将会在SQL Server错误日志目录下生成。
  2. MTL的大小在32位SQL Server是256MB+Max worker threads *0.5,所以大约是384MB除非你使用-g开关修改。
DECLARE @idoc int  

  
DECLARE @doc varchar(1000)
  

  
SET @doc ='
  

  

  
   
  
      
  
   
  

  

  
   
  
   
  
   
  

  
'
  

  
EXEC sp_xml_preparedocument @idoc OUTPUT, @doc
  

  
go 10000
  在执行几次之后,我们会收到以下错误。
Msg 6624, Level 16, State 12, Procedure sp_xml_preparedocument, Line 1  
XML document could not be created because server memory is low.
  从http://msdl.microsoft.com/download/symbols/debuggers/dbg_x86_6.11.1.404.msi下载和安装Windows Debugger来分析这个dump。
  步骤1:(加载这个内存dump文件来排除故障)
  打开Windbg。选择“File”菜单,选择“Open crash dump”,选择这个dump文件(SQLDump000#.mdmp)
  注意:当你得到异常或断言,在你的SQL Server错误日志里将会发现SQLDump000#.mdmp。
  步骤2:(设置符号目录为Microsoft symbols server)
  在命令窗口输入
  .sympath srv*c:\\Websymbols*http://msdl.microsoft.com/download/symbols;
  步骤3:(从Microsoft symbols server加载符号)
  输入.reload /f并点击回车。这将强制debugger立即加载所有符号。
  步骤4:(检查是否符号已加载)
  使用debugger命令lmvm验证是否符号加载到SQL Server。
  :028> lmvm sqlservr
  start    end      module name
  01000000 02ba8000   sqlservr   (pdb symbols)          c:\websymbols\sqlservr.pdb\93AACB610C614E1EBAB0FFB42031691D2\sqlservr.pdb
  Loaded symbol image file: sqlservr.exe
  Mapped memory image file: C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Binn\sqlservr.exe
  Image path: C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Binn\sqlservr.exe
  Image name: sqlservr.exe
  Timestamp:      Fri Oct 14 15:35:29 2005 (434F82E9)
  CheckSum:         01B73B9B
  ImageSize:      01BA8000
  File version:   2005.90.1399.0
  Product version:9.0.1399.0
  File flags:       0 (Mask 3F)
  File OS:          40000 NT Base
  File type:      1.0 App
  File date:      00000000.00000000
  Translations:   0409.04e4
  CompanyName:      Microsoft Corporation
  ProductName:      Microsoft SQL Server
  InternalName:   SQLSERVR
  OriginalFilename: SQLSERVR.EXE
  ProductVersion:   9.00.1399.06
  FileVersion:      2005.090.1399.00
  FileDescription:SQL Server Windows NT
  LegalCopyright:    Microsoft Corp. All rights reserved.
  LegalTrademarks:Microsoft is a registered trademark of Microsoft Corporation. Windows(TM) is a trademark of Microsoft Corporation
  Comments:         NT INTEL X86
  步骤5:(!address来限制内存信息)
  使用!address命令来限制dump进程的内存信息。
  0:028> !address -summary
  ——————– Usage SUMMARY ————————–
  TotSize (      KB)   Pct(Tots) Pct(Busy)   Usage
  686a7000 ( 1710748) : 81.58%    81.80%    : RegionUsageIsVAD
  579000 (    5604) : 00.27%    00.00%    : RegionUsageFree
  4239000 (   67812) : 03.23%    03.24%    : RegionUsageImage
  ea6000 (   15000) : 00.72%    00.72%    : RegionUsageStack
  1e000 (   120) : 00.01%    00.01%    : RegionUsageTeb
  122d0000 (297792) : 14.20%    14.24%    : RegionUsageHeap
  0 (       0) : 00.00%    00.00%    : RegionUsagePageHeap
  1000 (       4) : 00.00%    00.00%    : RegionUsagePeb
  1000 (       4) : 00.00%    00.00%    : RegionUsageProcessParametrs
  1000 (       4) : 00.00%    00.00%    : RegionUsageEnvironmentBlock
  Tot: 7fff0000 (2097088 KB) Busy: 7fa77000 (2091484 KB)
  ——————– Type SUMMARY ————————–
  TotSize (      KB)   Pct(Tots)Usage
  579000 (    5604) : 00.27%   :
  4239000 (   67812) : 03.23%   : MEM_IMAGE
  5fc000 (    6128) : 00.29%   : MEM_MAPPED
  7b242000 ( 2017544) : 96.21%   : MEM_PRIVATE
  ——————– State SUMMARY ————————–
  TotSize (      KB)   Pct(Tots)Usage
  1b7bd000 (450292) : 21.47%   : MEM_COMMIT
  579000 (    5604) : 00.27%   : MEM_FREE
  642ba000 ( 1641192) : 78.26%   : MEM_RESERVE

  Largest free region: Base 00000000 –>  查看RegionUsageHeap,它大约是297792KB,最大的空闲区域只有64KB。我们知道SQL Server不广泛使用堆内存,因此通常SQL Server堆内存分配将不会超过几MB。在这里消耗大约290MB,因此使用MTL的其他组件很容易失败。。
  让我们尝试理解为什么堆内存大约297792KB,尝试识别是否有一个模型。
  步骤6:(让我们使用!heap -s来显示堆内存摘要信息)
  0:028> !heap -s
  LFH Key                   : 0x672ddb11
  Heap   Flags   ReservCommitVirt   FreeList   UCRVirtLockFast
  (k)   (k)    (k)   (k) length      blocks cont. heap
  —————————————————————————–
  000d0000 00000002    1024    896    896      6   1   1    0      0   L
  001d0000 00008000      64   12   12   10   1   1    0      0
  002c0000 00001002    1088   96   96      2   1   1    0      0   L
  002e0000 00001002      64   52   52      3   2   1    0      0   L
  007c0000 00001002      64   64   64   56   1   0    0      0   L
  00d10000 00001002   256   24   24      8   1   1    0      0   L
  340b0000 00001002      64   28   28      1   0   1    0      0   L
  340c0000 00041002   256   12   12      4   1   1    0      0   L
  342a0000 00000002    1024   24   24      3   1   1    0      0   L
  34440000 00001002      64   48   48   40   2   1    0      0   L
  61cd0000 00011002   256   12   12      4   1   1    0      0   L
  61d10000 00001002      64   16   16      7   1   1    0      0   L
  61d20000 00001002      64   12   12      4   1   1    0      0   L
  62a90000 00001002    1024   1024   1024   1016   2   0    0      0   L
  62b90000 00001002    1024   1024   1024   1016   2   0    0      0   L
  62c90000 00001002   256   40   40      7   1   1    0      0   LFH
  00770000 00001002      64   16   16      2   2   1    0      0   L
  63820000 00001002      64   24   24      3   1   1    0      0   L
  63830000 00001001   102401024010240    160    21   0    0    bad
  64230000 00001001   102401024010240    160    21   0    0    bad
  64c30000 00001001   102401024010240    160    21   0    0    bad
  65630000 00001001   102401024010240    160    21   0    0    bad
  66030000 00001001   102401024010240    160    21   0    0    bad
  66a30000 00001001   102401024010240    160    21   0    0    bad
  67430000 00001001   102401024010240    160    21   0    0    bad
  68130000 00001001   102401024010240    160    21   0    0    bad
  68b30000 00001001   102401024010240    160    21   0    0    bad
  69530000 00001001   102401024010240    160    21   0    0    bad
  69f30000 00001001   102401024010240    160    21   0    0    bad
  6a930000 00001001   102401024010240    160    21   0    0    bad
  6b330000 00001001   102401024010240    160    21   0    0    bad
  6bd30000 00001001   102401024010240    160    21   0    0    bad
  6c730000 00001001   102401024010240    160    21   0    0    bad
  6d130000 00001001   102401024010240    160    21   0    0    bad
  6db30000 00001001   102401024010240    160    21   0    0    bad
  6e530000 00001001   102401024010240    160    21   0    0    bad
  6ef30000 00001001   102401024010240    160    21   0    0    bad
  6f930000 00001001   102401024010240    160    21   0    0    bad
  70330000 00001001   102401024010240    160    21   0    0    bad
  70d30000 00001001   102401024010240    160    21   0    0    bad
  7a160000 00001001   102401024010240    160    21   0    0    bad
  7ab60000 00001001   102401024010240    160    21   0    0    bad
  7b560000 00001001   102401024010240    160    21   0    0    bad
  7d0d0000 00001001   102401024010240    160    21   0    0    bad
  7e030000 00001001   102401024010240    160    21   0    0    bad
  7ea30000 00001001   102401024010240    160    21   0    0    bad
  67f90000 00001003   256   16   16   14   1   1    0    bad
  71850000 00001003   256      4      4      2   1   1    0    bad
  71890000 00001003   256      4      4      2   1   1    0    bad
  67fd0000 00001002      64   16   16      4   1   1    0      0   L
  718d0000 00001003   256   40   40      3   1   1    0    bad
  71910000 00001003   256      4      4      2   1   1    0    bad
  71950000 00001003   256      4      4      2   1   1    0    bad
  71990000 00001003   256      4      4      2   1   1    0    bad
  67ff0000 00001002      64   16   16      4   1   1    0      0   L
  719d0000 00001003    1792   1352   1352      5   2   1    0    bad
  71a10000 00001003   256      4      4      2   1   1    0    bad
  71a50000 00001003   256      4      4      2   1   1    0    bad
  71a90000 00001002      64   16   16      1   0   1    0      0   L
  —————————————————————————–
  如果你看以上输出,你可以清晰识别一个模型。创建了多个,每个是10MB。但是如何识别实际上是谁创建了它们?
  步骤7:
  让我们取出其中一个10MB的堆内存,并使用带-h参数的!heap显示在这个10MB堆内存里所有的内存分配条目。
  我获取的堆内存是63830000。
  0:028> !heap -h 63830000
  Index   AddressName      Debugging options enabled
  19:   63830000
  Segment at 63830000 to 64230000 (00a00000 bytes committed)
  Flags:                00001001
  ForceFlags:         00000001
  Granularity:          8 bytes
  Segment Reserve:      00100000
  Segment Commit:       00002000
  DeCommit Block Thres: 00000200
  DeCommit Total Thres: 00002000

  Total Free>
  Max. Allocation>  Lock Variable at:   00000000
  Next TagIndex:      0000
  Maximum TagIndex:   0000
  Tag Entries:          00000000
  PsuedoTag Entries:    00000000
  Virtual Alloc List:   63830050
  UCR FreeList:      63830588
  FreeList Usage:      00000000 00000000 00000000 00000000
  FreeList[ 00 ] at 63830178: 6422de88 . 638ad7e0      Unable to read nt!_HEAP_FREE_ENTRY structure at 638ad7e0
  (1 block )
  Heap entries for Segment00 in Heap 63830000
  63830608: 00608 . 00040 – busy (40)
  63830648: 00040 . 02808 – busy (2800)
  641b6698: 02808 . 02808 – busy (2800)
  ……………………………………
  ……………………………………
  ……………………………………
  ……………………………………
  步骤8:(让我们截取分配的堆内存条目之一,并尝试识别在它里面是什么)
  0:028> db 641b6698
  641b669801 05 01 05 93 01 08 00-49 61 6d 20 66 69 6c 69 ……..Iam fili
  641b66a86e 67 20 74 68 65 20 68-65 61 70 20 66 6f 72 20ng the heap for
  641b66b864 65 6d 6f 20 61 74 20-4d 53 53 51 4c 57 49 4bdemo at MSSQLWIK
  641b66c849 2e 43 4f 4d 00 00 00-00 00 00 00 00 00 00 00I.COM………..
  641b66d800 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00…………….
  641b66e800 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00…………….
  641b66f800 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00…………….
  641b670800 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00…………….
  0:028> db 63830648
  6383064801 05 08 00 89 01 08 00-49 61 6d 20 66 69 6c 69 ……..Iam fili
  638306586e 67 20 74 68 65 20 68-65 61 70 20 66 6f 72 20ng the heap for
  6383066864 65 6d 6f 20 61 74 20-4d 53 53 51 4c 57 49 4bdemo at MSSQLWIK
  6383067849 2e 43 4f 4d 00 00 00-00 00 00 00 00 00 00 00I.COM………..
  6383068800 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 …………….
  6383069800 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 …………….
  638306a800 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 …………….
  638306b800 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 …………….
  类似的,你可以dump多个堆分配来识别一个模型。
  现在如果你查看dump的内存,你会看到一个字符串可能对你识别创建堆内存的DLL有帮助。在以上堆里有一个模型。所有的堆分配都有以下字符串“I am filing the heap for demo at MSSQLWIKI.COM”
  注意:你可以使用L大小dump更多内存,使用db或dc命令的示例:db 63830648 L1500
  步骤9:
  让我们使用notepad打开加载到SQL Server里用于测试的DLL,查看是否有一个字符串匹配这个模型。

  是的,证明这个DLL导致了泄漏。在实践中你可能得去验证不同的堆分配来识别这个模型。
  这是一个在泄漏实际发生之后从内存dump找到泄漏。找到一个模型并识别分配该内存的模块并不总是容易的,在这里你可以使用像debug diagnostic tool、UMDH等工具来跟踪泄漏。在我的下一篇博文中,我将发表如何使用Debug diagnostics tools跟踪内存泄漏。https://mssqlwiki.com/2012/12/06/debugging-memory-leaks-using-debug-diagnostic-tool/


页: [1]
查看完整版本: SQL Server内存泄漏