平常的工作中,在衡量服务器的性能时,经常会涉及到几个指标,load、cpu、mem、qps、rt等。每个指标都有其独特的意义,很多时候在线上出现问题时,往往会伴随着某些指标的异常。大部分情况下,在问题发生之前,某些指标就会提前有异常显示。
在第一篇文章中,我们介绍了一个重要的指标就是负载(Load),其中我们提到Linux的负载高,主要是由于CPU使用、内存使用、IO消耗三部分构成。任意一项使用过多,都将导致服务器负载的急剧攀升。本文就来分析其中的第二项,内存使用。
内存是计算机中重要的部件之一,它是与CPU进行沟通的桥梁。计算机中所有程序的运行都是在内存中进行的,因此内存的性能对计算机的影响非常大。
内存(Memory)也被称为内存储器,其作用是用于暂时存放CPU中的运算数据,以及与硬盘等外部存储器交换的数据。
物理内存指通过物理内存条而获得的内存空间。即随机存取存储器(random access memory,RAM),是与CPU直接交换数据的内部存储器,也叫主存(内存)。
虚拟内存是计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换(也就是说,当物理内存不足时,可能会借用硬盘空间来充当内存使用)。与没有使用虚拟内存技术的系统相比,使用这种技术的系统使得大型程序的编写变得更容易,对真正的物理内存(例如RAM)的使用也更有效率。
Swap分区(即交换区)在系统的物理内存不够用的时候,把硬盘空间中的一部分空间释放出来,以供当前运行的程序使用。那些被释放的空间可能来自一些很长时间没有什么操作的程序,这些被释放的空间被临时保存到Swap分区中,等到那些程序要运行时,再从Swap分区中恢复保存的数据到内存中。
程序运行时的数据加载,线程并发,I/O缓冲等等,都依赖于内存,可用内存的大小,决定了程序是否能正常运行以及运行的性能。
在Linux机器上,有多个命令都可以查看机器的内存信息。其中包括free、top等。
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。
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命令查询到的数据。
total used free shared buffers cached Mem: 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)
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)
$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 复制代码
-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 复制代码
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行展示的就是内存的使用情况。并且也会按照进行展示不同进程的内存占用情况。十分好用。
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文件后,可以进行对象分析。如果有大量对象在持续被引用,并没有被释放掉,那就产生了内存泄露,就要结合代码,把不用的对象释放掉。