什么是内存

内存(Memory)是计算机的重要部件之一,也称内存储器和主存储器,它用于暂时存放 CPU 中的运算数据,与硬盘等外部存储器交换的数据

它是外存与 CPU 进行沟通的桥梁,计算机中所有程序的运行都在内存中进行,内存性能的强弱影响计算机整体发挥的水平。

只要计算机开始运行,操作系统就会把需要运算的数据从内存调到 CPU 中进行运算,当运算完成,CPU 将结果传送出来。

内存的运行也决定计算机整体运行快慢的程度。

Linux内存回收机制

为啥要回收:

  • 内核需要为任何时刻突发到来的内存申请提供足够的内存,以便cache的使用和其他相关内存的使用不至于让系统的剩余内存长期处于很少的状态。

  • 当真的有大于空闲内存的申请到来的时候,会触发强制内存回收。

内存回收针对的目标有两种,一种是针对 zone 的,另一种是针对一个 memcg 的,把针对 zone 的内存回收方式分为三种,分别是快速内存回收、直接内存回收、kswapd 内存回收。

查看Linux内存情况

查看/proc/meminfo

[root@test ~]# cat /proc/meminfo
MemTotal:       16166688 kB
MemFree:        14051412 kB
MemAvailable:   14772588 kB
Buffers:            2116 kB
Cached:          1073260 kB
SwapCached:            0 kB
Active:           770384 kB
Inactive:         698264 kB
Active(anon):     450156 kB
Inactive(anon):    76748 kB
Active(file):     320228 kB
Inactive(file):   621516 kB
Unevictable:           0 kB
Mlocked:               0 kB
SwapTotal:      33554428 kB
SwapFree:       33554428 kB
Dirty:               476 kB
Writeback:             0 kB
AnonPages:        393328 kB
Mapped:           153828 kB
Shmem:            133628 kB
Slab:             246448 kB
SReclaimable:     133892 kB
SUnreclaim:       112556 kB
KernelStack:       13472 kB
PageTables:        30496 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:    41637772 kB
Committed_AS:    4257776 kB
VmallocTotal:   34359738367 kB
VmallocUsed:      320696 kB
VmallocChunk:   34350426108 kB
HardwareCorrupted:     0 kB
AnonHugePages:    155648 kB
CmaTotal:              0 kB
CmaFree:               0 kB
HugePages_Total:       0
HugePages_Free:        0
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
DirectMap4k:      279276 kB
DirectMap2M:     6965248 kB
DirectMap1G:    11534336 kB

使用free命令查看

[root@test ~]# free -h
              total        used        free      shared  buff/cache   available
Mem:            15G        874M         13G        130M        1.2G         14G
Swap:           31G          0B         31G

参数说明:

  • total:总内存大小。

  • used:已经使用的内存大小(这里面包含 cached 和 buffers 和 shared 部分)。

  • free:空闲的内存大小。

  • shared:进程间共享内存(一般不会用,可以忽略)。

  • buffers:内存中写完的东西缓存起来,这样快速响应请求,后面数据再定期刷到磁盘上。

  • cached:内存中读完缓存起来内容占的大小(这部分是为了下次查询时快速返回)。

  • available:还可以被应用程序使用的物理内存大小,和 free 的区别是,free 是真正未被使用的内存,available 是包括 buffers、cached的。

  • Swap:硬盘上交换分区的使用大小。

Buffer和Cache

Cache(缓存),为了调高 CPU 和内存之间数据交换而设计,Buffer(缓冲)为了提高内存和硬盘(或其他 I/O 设备的数据交换而设计)。

Cache 主要是针对读操作设计的,不过Cache概念可能容易混淆,我理解为 CPU 本身就有 Cache,包括一级缓存、二级缓存、三级缓存,我们知道 CPU 所有的指令操作对接的都是内存,而 CPU 的处理能力远高于内存速度,所以为了不让 CPU 资源闲置,Intel等公司在 CPU 内部集成了一些 Cache,但毕竟不能放太多电路在里面,所以这部分 Cache 并不是很大,主要是用来存放一些常用的指令和常用数据,真正大部分 Cache的数据应该是占用内存的空间来缓存请求过的数据,即上面的 Cached 部分(这部分纯属个人理解,正确与否有待考证)。

Buffer 主要是针对写操作设计的,更细的说是针对内存和硬盘之间的写操作来设计的,目的是将写的操作集中起来进行,减少磁盘碎片和硬盘反复寻址过程,提高性能。

在 Linux 系统内部有一个守护进程会定期清空 Buffer 中的内容,将其写入硬盘内,当手动执行 sync 命令时也会触发上述操作。

Swap

虽然现在的内存已经变得非常廉价,但是 swap 仍然有很大的使用价值,合理的规划和使用 swap 分区,对系统稳定运行至关重要。

Linux 下可以使用文件系统中的一个常规文件或者一个独立分区作为交换空间使用。同时 Linux 允许使用多个交换分区或者交换文件。

内存泄漏和内存溢出

内存溢出(OOM,out of memory),是指程序在申请内存时,没有足够的内存空间供其使用,出现 out of memory;比如申请了一个integer,但给它存了 long 才能存下的数,那就是内存溢出。

内存泄露(memory leak),是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。

如何判断内存泄露

jstat -gcutil PID,观察 Old 这个参数,如果每次执行完 FULLGC 之后 Old 区的值一次比一次升高,就可以判断为发生了内存泄漏。

如何判断内存溢出

Heap Dump(堆转储文件)它是一个Java进程在某个时间点上的内存快照。Heap Dump 是有着多种类型的。不过总体上heap dump在触发快照的时候都保存了java对象和类的信息。通常在写 heap dump 文件前会触发一次 FullGC,所以 heap dump 文件中保存的是 FullGC 后留下的对象信息。

通过设置如下的 JVM 参数,可以在发生 OutOfMemoryError 后获取到一份HPROF二进制 Heap Dump 文件:

-XX:+HeapDumpOnOutOfMemoryError

生成的文件会直接写入到工作目录。

💡注意:该方法需要JDK5以上版本。

转存堆内存信息后,需要对文件进行分析,从而找到 OOM 的原因。可以使用以下方式:

  • mat:eclipse memory analyzer, 基于 eclipse RCP 的内存分析工具。具体使用参考:http://www.eclipse.org/mat/

  • jhat:JDK自带的java heap analyze tool,可以将堆中的对象以 html 的形式显示出来,包括对象的数量,大小等等,并支持对象查询语言 OQL,分析相关的应用后,可以通过http://localhost:7000来访问分析结果。不推荐使用。

OOM常见原因及解决方案

可参考高手总结的9种 OOM 常见原因及解决方案

释放内存

在 Linux 系统下,我们一般不需要去释放内存,因为系统已经将内存管理的很好。但是凡事也有例外,有的时候内存会被缓存占用掉,导致系统使用 SWAP 空间影响性能,例如当你在 Linux 下频繁存取文件后,物理内存会很快被用光,当程序结束后,内存不会被正常释放,而是一直作为 caching。此时就需 要执行释放内存(清理缓存)的操作了。

释放内存操作:

sync  # 强制将内存中的缓存写入磁盘

echo 数字 > /proc/sys/vm/drop_caches #数字可以是0-3的整数

数字含义:

  • 0:不释放(系统默认值)

  • 1:释放页缓存

  • 2:释放dentries和inodes

  • 3:释放所有缓存