转载

线上服务器内存分析及问题排查

平常的工作中,在衡量服务器的性能时,经常会涉及到几个指标,load、cpu、mem、qps、rt等。每个指标都有其独特的意义,很多时候在线上出现问题时,往往会伴随着某些指标的异常。大部分情况下,在问题发生之前,某些指标就会提前有异常显示。

在第一篇文章中,我们介绍了一个重要的指标就是负载(Load),其中我们提到Linux的负载高,主要是由于CPU使用、内存使用、IO消耗三部分构成。任意一项使用过多,都将导致服务器负载的急剧攀升。本文是该系列的第三篇,来分析一下影响机器负载的几个原因中的第二项,内存使用。

线上服务器内存分析及问题排查

什么是内存

内存是计算机中重要的部件之一,它是与CPU进行沟通的桥梁。计算机中所有程序的运行都是在内存中进行的,因此内存的性能对计算机的影响非常大。

内存(Memory)也被称为内存储器,其作用是用于暂时存放CPU中的运算数据,以及与硬盘等外部存储器交换的数据。

物理内存

物理内存指通过物理内存条而获得的内存空间。即随机存取存储器(random access memory,RAM),是与CPU直接交换数据的内部存储器,也叫主存(内存)。

虚拟内存

虚拟内存是计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换(也就是说,当物理内存不足时,可能会借用硬盘空间来充当内存使用)。与没有使用虚拟内存技术的系统相比,使用这种技术的系统使得大型程序的编写变得更容易,对真正的物理内存(例如RAM)的使用也更有效率。

Swap分区

Swap分区(即交换区)在系统的物理内存不够用的时候,把硬盘空间中的一部分空间释放出来,以供当前运行的程序使用。那些被释放的空间可能来自一些很长时间没有什么操作的程序,这些被释放的空间被临时保存到Swap分区中,等到那些程序要运行时,再从Swap分区中恢复保存的数据到内存中。

程序运行时的数据加载,线程并发,I/O缓冲等等,都依赖于内存,可用内存的大小,决定了程序是否能正常运行以及运行的性能。

查看内存使用情况

在Linux机器上,有多个命令都可以查看机器的内存信息。其中包括free、top等。

free命令

free命令可以显示Linux系统中空闲的、已用的物理内存,swap分区以及被内核缓冲区内存。在Linux系统监控的工具中,free命令是最经常使用的命令之一。

$free 
             total       used       free     shared    buffers     cached 
Mem:       8388608    2926968    5461640          0          0    1654392 
-/+ buffers/cache:    1272576    7116032 
Swap:     16777208          0   16777208 

上图中,一共有3行6列数据,行数据的意义如下: Mem 行是内存的使用情况。 -/+ buffers/cache 行是物理内存的缓存统计情况。 Swap 行是交换空间的使用情况。

前面分别介绍过了物理内存和Swap分区。这里再介绍一下buffers和cache。

buffer与cache的区别

  • A buffer is something that has yet to be "written" to disk.
  • A cache is something that has been "read" from the disk and stored for later use.

简单点说:

buffers 就是存放要输出到disk(块设备)的数据,缓冲满了一次写,提高IO性能(内存 -> 磁盘)

cached 就是存放从disk上读出的数据,常用的缓存起来,减少IO(磁盘 -> 内存)

buffer 和 cache,两者都是RAM中的数据。简单来说,buffer是即将要被写入磁盘的,cache是被从磁盘中读出来的。

介绍完了buffer和cache的区别,接下来分析下free命令查询到的数据。

Mem行

total       used       free     shared    buffers     cached 
em:       8388608    2926968    5461640          0          0    1654392 

这一行展示物理内存的整体情况。

Total:8388608。表示物理内存总大小。

Used :2926968。表示总计分配给缓存(包含buffers 与cache )使用的数量,但其中可能部分缓存并未实际使用。

Free :5461640。表示未被分配的内存。

Shared:0。共享内存,一般系统不会用到。

Buffers:0。系统分配但未被使用的buffers 数量。

Cached:1654392。系统分配但未被使用的cache 数量。

  • total(Mem) = used(Mem) + free(Mem)

-/+ buffers/cache 行

total       used       free     shared    buffers     cached 
-/+ buffers/cache:    1272576    7116032 

Used:1272576。 表示实际使用的buffers 与cache 总量,也是实际使用的内存总量。

Free:7116032。 未被使用的buffers 与cache 和未被分配的内存之和,这就是系统当前实际可用内存。

used(-/+ buffers/cache) = used(Mem) - cached(Mem) - buffers(Mem)

free(-/+ buffers/cache) = free(Mem) + cached (Mem)+ buffers(Mem)

Swap 行

$free 
             total       used       free     shared    buffers     cached 
Swap:     16777208          0   16777208 

Total:16777208。Swap内存总大小。

Used:0。表示已分配的Swap大小。

Free:16777208。表示未被分配的内存。

接下来,再来整体看一下数据。

$free 
             total       used       free     shared    buffers     cached 
Mem:       8388608    2926968    5461640          0          0    1654392 
-/+ buffers/cache:    1272576    7116032 
Swap:     16777208          0   16777208 

机器上实际可用内存大小:

Free(-/+ buffers/cache)= Free(Mem)+buffers(Mem)+Cached(Mem); 
 
                              7116032 = 5461640 + 0+ 1654392 

已经分配的内存大小:

Used(Mem) = Used(-/+ buffers/cache)+ buffers(Mem) + Cached(Mem) 
 
          2926968 = 1272576 + 0 + 1654392 

物理内存总大小

total(Mem) = used(-/+ buffers/cache) + free(-/+ buffers/cache) 
 
                 8388608 = 1272576 + 7116032 

总结一下,整个机器的总内存大小8388608,其中已经分配的内存有2926968,还未分配的内存有5461640。而分配的2926968中,有1654392还没有使用,有1272576已经用掉了。当前机器中还有7116032内存可以使用。

free命令参数

-m 以M为单位显示内存

$free -m 
             total       used       free     shared    buffers     cached 
Mem:          8192       2802       5389          0          0       1559 
-/+ buffers/cache:       1243       6948 
Swap:        16383          0      16383 

-g 以G为单位显示内存

$free -g 
             total       used       free     shared    buffers     cached 
Mem:          8          2          5         0         0           1 
-/+ buffers/cache:       1          6 
Swap:        16          0          16 

-s 2持续的观察内存的状况,每隔2秒打印一次

$free -s 2 
         total       used       free     shared    buffers     cached 
Mem:       8388608    2873128    5515480          0          0    1600588 
-/+ buffers/cache:    1272540    7116068 
Swap:     16777208          0   16777208 
 
             total       used       free     shared    buffers     cached 
Mem:       8388608    2873168    5515440          0          0    1600628 
-/+ buffers/cache:    1272540    7116068 
Swap:     16777208          0   16777208 

除了free ,还可以在Linux下可以使用/proc/meminfo文件查看操作系统内存的使用状态,其实,free命令的内容也是来自于/proc/meminfo文件。

top命令

top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器。

在前面两篇文章中介绍过使用top命令查看Load Avg和CPU利用率。top还会打印的一部分信息就是内存情况。

top - 17:49:32 up 2 days,  6:25,  1 user,  load average: 0.01, 0.09, 0.12 
Tasks:  30 total,   1 running,  29 sleeping,   0 stopped,   0 zombie 
Cpu(s):  0.1%us,  0.0%sy,  0.0%ni, 88.0%id,  3.8%wa,  0.0%hi,  0.0%si,  8.1%st 
Mem:   8388608k total,  2884716k used,  5503892k free,        0k buffers 
Swap: 16777208k total,        0k used, 16777208k free,  1612080k cached 
 
   PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND 
 85690 admin     20   0 5138m 1.1g  47m S  2.3 13.9  93:28.92 java 

上面的Mem行和Swap行展示的就是内存的使用情况。并且也会按照进行展示不同进程的内存占用情况。十分好用。

Java Web应用内存占用飙高排查思路

JVM以一个进程(Process)的身份运行在Linux系统上,对于Linux来说,JVM不过是一个具有自助管理内存的乖孩子而已。

一般在应用启动时都可以通过JVM参数来设置JVM内存的大小。如果超过这个限制就会抛出异常。所以,我们比较常见的内存占用过高问题,最显著的现象就是抛出各种OutOfMemoryError。

有一种可能导致直接内存,也就是Linux的物理内存过高的情况,就是NIO的使用。NIO引入了一种基于通道与缓冲区的IO方式,他可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。

所以,在使用NIO的时候,要特别小心,避免导致机器内存被挤满。

导致JVM中内存占用飙高的原因可能有很多。最常见的就是内存泄露。

内存泄露排查思路

1、使用top命令,查看占用内存较高的进程ID。

➜  ~ top 
 
PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND 
3331 admin     20   0 7127m 2.6g  38m S 10.7 90.6  10:20.26 java 

发现PID为3331的进程占用内存 90.6%。而且是一个Java进程,基本断定是程序问题。

2、使用jmap查看内存情况,并分析是否存在内存泄露。

jmap -heap 3331:查看java 堆(heap)使用情况 
 
jmap -histo 3331:查看堆内存(histogram)中的对象数量及大小 
 
jmap -histo:live 3331:JVM会先触发gc,然后再统计信息 
 
jmap -dump:format=b,file=heapDump 3331:将内存使用的详细情况输出到文件 

得到堆dump文件后,可以进行对象分析。如果有大量对象在持续被引用,并没有被释放掉,那就产生了内存泄露,就要结合代码,把不用的对象释放掉。

【本文是51CTO专栏作者Hollis的原创文章,作者微信公众号Hollis(ID:hollischuang)】

戳这里,看该作者更多好文

原文  http://zhuanlan.51cto.com/art/201808/581048.htm
正文到此结束
Loading...