葵花解穴——应用卡顿 OOM 定位

1,328 阅读9分钟

异常场景

在使用软件进行多路视频流渲染时明显出现软件卡顿闪退现象,启动后监控系统管理器内存,CUP占用飙升,两者占用率达 90% 以上后将出现无响应闪退情况,疑似触发 OOM;

期间通过自定义命令打印内存使用

while ($true) {
    $memInfo = Get-WmiObject -Class Win32_OperatingSystem
    $availableMemory = [math]::round($memInfo.FreePhysicalMemory / 1024, 2)
    $totalMemory = [math]::round($memInfo.TotalVisibleMemorySize / 1024, 2)
    $usedMemory = [math]::round(($totalMemory - $availableMemory), 2)
    
    Write-Output "Total Memory: $totalMemory MB"
    Write-Output "Used Memory: $usedMemory MB"
    Write-Output "Available Memory: $availableMemory MB"
    Write-Output "----------"
    
    Start-Sleep -Seconds 5
}

内存回收记录

Total Memory: 16059.28 MB  
Used Memory: 15740.98 MB  
Available Memory: 318.3 MB  
----------  
Total Memory: 16059.28 MB  
Used Memory: 15158.36 MB  
Available Memory: 900.92 MB  
----------  
Total Memory: 16059.28 MB  
Used Memory: 15445.71 MB  
Available Memory: 613.57 MB  
----------  
Total Memory: 16059.28 MB  
Used Memory: 15735.45 MB  
Available Memory: 323.83 MB  
----------  
Total Memory: 16059.28 MB  
Used Memory: 15578.71 MB  
Available Memory: 480.57 MB  
----------  
Total Memory: 16059.28 MB  
Used Memory: 15788.57 MB  
Available Memory: 270.71 MB  
----------  
Total Memory: 16059.28 MB  
Used Memory: 15461.94 MB  
Available Memory: 597.34 MB  
----------  
Total Memory: 16059.28 MB  
Used Memory: 15679.18 MB  
Available Memory: 380.1 MB  
----------  
Total Memory: 16059.28 MB  
Used Memory: 16037.23 MB  
Available Memory: 22.05 MB  
----------  
Total Memory: 16059.28 MB  
Used Memory: 15614.89 MB  
Available Memory: 444.39 MB  
----------  
Total Memory: 16059.28 MB  
Used Memory: 14949.76 MB  
Available Memory: 1109.52 MB  
----------  
Total Memory: 16059.28 MB  
Used Memory: 15263.12 MB  
Available Memory: 796.16 MB  
----------  
Total Memory: 16059.28 MB  
Used Memory: 15735.75 MB  
Available Memory: 323.53 MB  
----------  
Total Memory: 16059.28 MB  
Used Memory: 14907.19 MB  
Available Memory: 1152.09 MB  
---------- 

日志分析

内存使用量有波动,显示出系统正在积极管理内存资源以避免 OOM。

从数据中可以观察到 Used MemoryAvailable Memory 之间的显著波动。这些波动可以是多种系统行为的结果,尤其是在系统尝试管理内存资源避免 OOM(Out of Memory)错误时。应该要经历以下几个阶段

1. 内存回收

当系统内存使用率过高时,操作系统会触发内存回收机制来释放不再需要的内存资源。这可能会导致 Available Memory 突然增加,而 Used Memory 突然减少。这是通过释放不再使用的内存块、清理缓存、交换文件等操作实现的。

2. 申请虚拟内存

Windows 系统在物理内存不足时,会使用虚拟内存(Page File)。当系统需要更多内存而物理内存不足时,部分内存内容会被交换到磁盘上的虚拟内存中,从而释放物理内存。这会导致 Available Memory 突然增加,尽管 Used Memory 可能没有显著变化。

3. 应用程序崩溃或终止

如果某个应用程序使用了大量内存并且达到了系统能承受的极限,操作系统可能会强制终止该应用程序以释放内存资源。这会导致 Used Memory 突然减少,Available Memory 突然增加。

4. 内存泄漏检测和处理

系统或某些应用程序可能会检测到内存泄漏,并尝试回收这些泄漏的内存。这会导致 Available Memory 突然增加,而 Used Memory 突然减少。

结论

内存波动显示系统正在积极通过回收不必要的内存、使用虚拟内存等方式来避免 OOM 错误。如果这些操作不能满足需求,应用可能会经历以下周期:

  1. 内存使用量逐渐增加。
  2. 系统尝试回收内存。
  3. 内存仍然不足时,应用卡顿。
  4. 最终系统可能会强制终止某些应用来释放内存,导致闪退。

OOM 问题

OOM 除了引发应用闪退,还可能引发系统无响应、性能显著下降等问题。这些都与操作系统的内存管理和垃圾回收机制密切相关。

1. 应用程序卡顿

对于一些编程语言和运行时环境(如 .NET 和 Java),垃圾回收是内存管理的一部分。垃圾回收器定期检查和回收不再使用的内存。这会导致:

  • 暂停应用程序:垃圾回收器运行时,会暂停所有应用程序线程,以便安全地回收内存。
  • 高 CPU 占用:垃圾回收过程需要计算和遍历内存,可能会导致 CPU 使用率上升。

2. 系统卡顿

当系统资源紧张时,各个进程会争夺有限的 CPU、内存和 I/O 资源。这会导致:

  • 系统响应变慢:各个进程需要等待资源变得可用。
  • 高上下文切换:频繁的进程切换会增加 CPU 开销,从而进一步降低系统性能。

页内存管理

Windows系统通过虚拟内存、页面置换和内存回收等机制管理内存。在发生OOM之前,系统会经历一系列优化步骤,如内存警告、页面置换、内存回收和进程终止等。

  • 页缓存和预取:

    • 系统会缓存最近访问的页面,并预取一些可能会被访问的页面,以提高性能。
  • 页面置换(Page Replacement) :

    • 当物理内存不足时,系统会选择一些页面移到页面文件中,腾出物理内存。这些页面可能是最近不常访问的页面。
    • 常见的页面置换算法包括LRU(Least Recently Used)、FIFO(First In First Out)等。
  • 内存回收:

    • 系统会定期回收未使用的内存,如未引用的对象或释放的内存块。

OOM 的一些解决方案

增加物理内存

原因:当系统物理内存不足时,操作系统会频繁使用页面交换,将内存中的数据写入硬盘上的页面文件。这会导致系统性能显著下降,因为硬盘读写速度远慢于内存访问速度。 解决方案:

通过增加物理内存(RAM)容量,可以显著提升系统的整体性能,减少页面交换的频率。 评估需求:根据应用程序和工作负载的需求,合理规划和配置内存容量。例如,运行大型数据库或虚拟机时,需要更高的内存容量。

使用SSD

原因:相较于传统的HDD,SSD有更快的读写速度,可以显著减少页面交换带来的性能损失。

替换硬盘:将系统硬盘从HDD升级为SSD,可以大幅提升系统启动速度和应用程序加载速度。 混合存储方案:在预算有限的情况下,可以采用SSD和HDD的混合存储方案,将操作系统和常用应用程序安装在SSD上,将大文件和不常用数据存储在HDD上。 优化SSD使用:定期进行SSD维护,如固件更新、TRIM命令执行,确保SSD性能稳定。

系统层面优化

操作系统层次

原因:不必要的应用程序和后台进程会占用大量内存资源,导致系统整体内存压力增大。

  • 关闭不必要的应用程序:
  • 定期检查和关闭不再使用的应用程序,释放被占用的内存资源。
  • 优化启动项,通过系统配置工具(如Windows的Task Manager)管理和优化开机启动项;

应用层面优化

应用层(尤其是使用托管语言如Java、.NET的应用程序)

原因:垃圾回收机制是某些编程语言内存管理的一部分,频繁的垃圾回收会导致应用程序暂停和CPU使用率上升,从而影响系统性能。

编程方面:

  • 选择合适的数据结构:使用适合需求的数据结构,避免内存浪费。
  • 减少对象创建:尽量重用对象,避免频繁创建和销毁对象。
  • 内存泄漏检测:使用工具检测和修复内存泄漏问题。
  • 代码优化:优化算法,减少不必要的计算和内存分配。

垃圾回收优化

  • 调整垃圾回收参数:根据应用特点调整垃圾回收参数和策略。
  • 内存管理优化:通过优化代码和内存管理,减少不必要的对象创建和销毁,降低垃圾回收的频率。
  • 使用合适的垃圾回收器:选择适合应用负载的垃圾回收器,如G1、ZGC(Java)等。
  • 监控和调试:定期监控垃圾回收行为,及时调整策略。

优化对比

以投入成本对比输出

优化方式实现难度人工成本作用优先级
关闭不必要的应用程序中等1
优化启动项中等2
监控和调试垃圾回收行为3
减少对象创建4
选择合适的数据结构5
使用合适的垃圾回收器6
内存泄漏检测7
代码优化8

综合:

内存优化方案及其优先级排序

  1. 关闭不必要的应用程序和优化启动项 作为最容易实现且成本最低的方法,可以优先实施。定期检查并关闭不再使用的应用程序,使用系统配置工具管理和优化开机启动项,能迅速释放内存资源,提高系统响应速度。

  2. 使用合适的垃圾回收器和监控调试垃圾回收行为 根据应用需求选择合适的垃圾回收器(如 Java 中的 G1、ZGC),并使用监控工具(如 VisualVM、JConsole)监控垃圾回收行为,调整垃圾回收参数。虽然成本高,但能迅速优化内存管理效果显著,提高系统稳定性,适合有技术背景的团队。

  3. 内存泄漏检测和代码优化 使用内存泄漏检测工具(如 Valgrind、LeakCanary、VisualVM)识别并修复内存泄漏问题,选择合适的数据结构,减少对象创建,优化算法,降低内存和 CPU 的消耗。尽管成本高且耗时多,但对应用性能提升作用最大,适合有长期优化计划的项目。

  4. 局部重构 在编程层面进行代码重构,优化数据结构和算法,减少内存和 CPU 的消耗。虽然成本高且耗时长,但能显著提升应用性能,解决深层次的性能问题,适合需要进行深度性能优化的项目。