多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

CPU核数与内存使用全解析:从任务管理器到cgroup与JVM

CPU核数与内存使用全解析:从任务管理器到cgroup与JVM 上周帮同事看一台开发机IDE 一编译就风扇狂转任务管理器里 CPU 到 100%内存却只显示 60%。他问我这台机器到底几核内存是不是不够这俩问题看起来简单真到排查时每个人看到的数字都可能不一样。任务管理器、资源监视器、Linux 的 free、JVM 的 Runtime、容器里的 cgroup、硬件监控软件给出的 CPU核数 和 内存使用情况 经常对不上。不是工具骗人而是它们看的层级不同有的看物理硬件有的看操作系统可见资源有的看进程地址空间还有的看 cgroup 配额。这篇内容就围绕“查看CPU核数、内存使用情况”这件事把 Windows、Linux、macOS、Android、JVM、容器、Spark、嵌入式开发板等常见场景一次讲透。适合运维、后端、客户端、测试、硬件爱好者也适合刚接触性能排查的新手。我会从概念开始接着给可直接复制的命令、参数计算、排查表格和踩坑经验。你不需要全部记住但遇到机器卡、内存涨、进程被杀、容器 OOM、编译失败时能快速找到该看哪一项。1. 先搞清楚CPU核数和内存使用情况到底在看什么1.1 物理核、逻辑核、可用CPU为什么数字总对不上CPU核数至少有四个层次物理 CPU 插槽数、每插槽物理核心数、超线程后的逻辑处理器数、当前进程或容器实际可用的 CPU 数。Windows 任务管理器性能页看到的“内核”通常是物理核“逻辑处理器”是超线程后的逻辑核。比如 1 颗 8 核 16 线程的 CPU任务管理器会显示 8 个内核、16 个逻辑处理器。Linux 的lscpu会分别列出 Socket(s)、Core(s) per socket、Thread(s) per core把这三项乘起来才是逻辑 CPU 总数。很多人只记“几核”但程序真正能用多少核还受 CPU 亲和性、cgroup、Kubernetes limits、虚拟机 vCPU、JVM 参数影响。比如宿主机有 64 个逻辑核Docker 容器只给了--cpus2容器里跑nproc可能仍显示 64但 cgroup 配额只有 2 个 CPU。Java 的Runtime.getRuntime().availableProcessors()在老版本 JDK 中可能读不到容器配额导致线程池开得过大。所以看 CPU核数不能只看一个数字要问物理机多少、操作系统看到多少、当前进程被允许用多少。还有一个常见坑虚拟机“CPU 就绪等待”高但任务管理器里 CPU 占用不高。因为虚拟机想跑但宿主机物理 CPU 被其他虚拟机占满它拿不到时间片。此时客户机内部看到的 CPU 使用率是偏低的真正的瓶颈在宿主机。排查这类问题要同时看宿主机和虚拟机两层的 CPU 就绪、等待、偷取时间。Linux 里%steal、%wait就是重要线索。1.2 物理内存、虚拟内存、缓存、Swap和堆外内存内存使用情况比 CPU核数更容易误判。物理内存是插在主板上的内存条容量操作系统可见内存可能因为 32 位限制、集成显卡占用、硬件保留而减少虚拟内存是进程看到的地址空间不等于真实占用缓存和缓冲区是内核拿空闲内存做磁盘加速需要时能回收Swap 或页面文件是磁盘上的交换空间速度远慢于内存堆外内存是 JVM、Netty、数据库等绕过 Java 堆直接向操作系统申请的内存。Linux 的free -h里最该看的是available不是free。free是彻底没被使用的内存buff/cache看起来很大但大部分可回收。available是内核估算的、在不触发大量换页的情况下还能分配给新应用的内存。如果available很低、si/so持续非零说明内存压力已经很大。Windows 任务管理器的“使用中”包含压缩内存“已提交”包含页面文件承诺量二者不是同一个东西。内存和缓存也经常被混为一谈。缓存是为了加速后续读取通常可回收内存泄漏是程序申请后不释放导致占用持续上涨且不回落。看到 Linux 的 cached 很高不用慌看到 Java 进程 RSS 比-Xmx大也不用慌因为 RSS 包含堆、元空间、线程栈、代码缓存、直接内存、JNI、mmap 文件等。真正要警惕的是长期趋势内存占用是否随请求量线性上涨Full GC 后是否仍不下降available是否持续减少。1.3 “占用不高但卡”的常见误判GPU、CPU、内存占用都不高但系统卡这类问题最折磨人。常见原因包括磁盘 I/O 饱和、网络延迟、锁竞争、页面文件频繁读写、DPC/中断延迟、驱动问题、温度降频、显存不足、电源模式限制。任务管理器只看 CPU、内存、磁盘、网络四个大项很多延迟藏在“平均占用”里。比如一块机械盘占用只有 30%但响应时间已经几百毫秒系统照样卡。Windows 上可以用资源监视器看磁盘队列长度和响应时间用 LatencyMon 看 DPC 延迟用性能监视器看Processor Queue Length、Avg. Disk sec/Transfer。Linux 上用iostat -x 1看%util、await、aqu-sz用pidstat -d 1看进程级 I/O用perf top看热点函数。内存方面vmstat 1的si/so如果持续非零说明正在换页哪怕内存占用显示 70%也可能已经卡到无法忍受。还有一个高频误判把“C 盘空间不足”说成“清理电脑 C 盘内存”。C 盘是磁盘内存是 RAM二者不同。C 盘满了会影响页面文件、临时文件、日志、编译缓存进而导致程序申请内存失败或系统变慢。所以排查时先分清你要解决的是内存不足还是磁盘空间不足还是虚拟内存/页面文件不足。2. Windows下查看CPU核数与内存任务管理器只是入口2.1 图形界面任务管理器、资源监视器、系统信息Windows 最快的方式是Ctrl Shift Esc打开任务管理器切到“性能”页。CPU 区域能看到型号、内核、逻辑处理器、基准速度、插槽、虚拟化状态内存区域能看到总容量、速度、插槽使用、已使用、可用、已提交、缓存、分页池、非分页池。右键 CPU 图可以“将图形更改为逻辑处理器”这样能看到每个逻辑核的负载排查单核跑满、线程绑定问题很有用。资源监视器更适合排查异常进程。在任务管理器性能页底部打开“打开资源监视器”或运行resmon。CPU 页能看到每个进程的平均 CPU、线程、服务、句柄内存页能看到硬错误、提交、工作集、可共享、专用。工作集是进程当前实际占用的物理内存专用工作集更能反映进程独享内存。如果某个进程的硬错误持续增加说明它频繁从页面文件或磁盘换入内存。msinfo32系统信息能看到处理器、物理内存总量、可用物理内存、虚拟内存、页面文件空间。dxdiag能看系统型号、处理器、内存但对多通道、频率信息不如任务管理器详细。图形界面的优点是直观缺点是刷新慢、隐藏细节多。比如“为硬件保留的内存”在任务管理器内存页能看到但具体是谁保留的需要进一步用命令行或驱动工具排查。注意任务管理器里的“已提交”不是物理内存占用而是操作系统承诺给进程的虚拟内存总量包含页面文件。看到“已提交”接近上限不一定物理内存耗尽但说明虚拟地址空间或页面文件需要关注。2.2 命令行wmic、PowerShell、systeminfo的准确用法图形界面适合快速看命令行适合记录、批量、远程排查。Windows 10/11 仍可用wmic但微软已逐步弃用建议优先 PowerShell。# CPU 物理核和逻辑核 Get-CimInstance Win32_Processor | Select-Object Name, NumberOfCores, NumberOfLogicalProcessors, MaxClockSpeed, SocketDesignation # 物理内存总量、可见内存、空闲内存 Get-CimInstance Win32_ComputerSystem | Select-Object TotalPhysicalMemory Get-CimInstance Win32_OperatingSystem | Select-Object TotalVisibleMemorySize, FreePhysicalMemory, TotalVirtualMemorySize, FreeVirtualMemory # 内存条详细容量、速度、配置速度、厂商、插槽 Get-CimInstance Win32_PhysicalMemory | Select-Object BankLabel, DeviceLocator, Capacity, Speed, ConfiguredClockSpeed, Manufacturer, PartNumber如果要用传统命令wmic cpu get Name,NumberOfCores,NumberOfLogicalProcessors,MaxClockSpeed wmic computersystem get TotalPhysicalMemory wmic os get FreePhysicalMemory,TotalVisibleMemorySize,FreeVirtualMemory,TotalVirtualMemorySize wmic memorychip get BankLabel,Capacity,Speed,ConfiguredClockSpeed,Manufacturer systeminfo | findstr /C:处理器 /C:物理内存 /C:虚拟内存wmic memorychip的Speed是内存条标称频率ConfiguredClockSpeed是当前运行频率。很多人买了 3200 内存Speed显示 3200但ConfiguredClockSpeed只有 2133 或 2666说明 BIOS 里没开 XMP/EXPO/DOCP。要确认 3200 频率内存是否真正运行在 3200必须看ConfiguredClockSpeed。另外任务管理器内存页的速度栏也会显示当前速度。PowerShell 还能看性能计数器Get-Counter \Memory\Available MBytes Get-Counter \Memory\Committed Bytes Get-Counter \Processor(_Total)\% Processor Time如果“用户拒绝访问内存文件权限”导致工具打不开通常是权限不足。任务管理器、资源监视器、Process Explorer、RAMMap 读取内存信息一般需要管理员权限。右键以管理员身份运行如果还是拒绝检查组策略、杀毒软件、文件所有权和 ACL。企业环境中安全软件可能阻止内存转储或进程内存读取这是正常防护不要强行绕过。2.3 页面文件、内存压缩和为硬件保留的内存Windows 的页面文件pagefile.sys相当于 Linux 的 Swap。任务管理器“已提交”上限通常等于物理内存加页面文件。页面文件不是越大越好也不是越小越好。太小可能导致内存分配失败太大可能占用 C 盘空间。一般让系统自动管理即可特殊场景可以手动设置固定大小。查看页面文件Get-CimInstance Win32_PageFileUsage | Select-Object Name, AllocatedBaseSize, CurrentUsage, PeakUsage Get-CimInstance Win32_PageFileSetting | Select-Object Name, InitialSize, MaximumSizeWindows 10/11 默认开启内存压缩。它把不活跃的内存页压缩后放在内存里减少页面文件写入。任务管理器“使用中”包含压缩内存。网上有很多“Win10 关闭内存压缩”的教程命令是管理员 PowerShell# 查看状态 Get-MMAgent # 关闭内存压缩 Disable-MMAgent -mc # 开启内存压缩 Enable-MMAgent -mc关闭后需要重启。我的经验是除非内存压缩导致 CPU 占用异常、兼容性问题或你在做特定测试否则不要随手关闭。关闭后可能增加页面文件读写机械硬盘或 eMMC 设备反而更卡。内存压缩是拿 CPU 换内存现代 CPU 压缩开销通常可接受。“为硬件保留的内存”是任务管理器里经常让人疑惑的项。它表示被固件、驱动、集成显卡、PCIe 设备、内存映射 I/O 等占用的地址空间不一定真的插了内存条却不能用。常见于集成显卡占用、32 位系统、BIOS 设置、雷电设备预留。如果保留量特别大比如 8GB 内存保留 2GB可以检查集成显卡显存设置、BIOS 内存重映射、驱动版本。普通独显机器保留几百 MB 属于正常。2.4 Windows异常进程排查Defender、微信小程序、Edge、SQL ServerAntimalware Service Executable是 Windows Defender 的核心进程扫描时吃 CPU 和内存很常见。搜索词里常写成antimalware service executa占内存其实就是它。排查方法打开 Windows 安全中心查看保护历史记录和扫描计划把开发目录、编译输出目录、虚拟机磁盘目录加入排除项避免同时跑多个全盘扫描。不要直接关闭 Defender企业环境有合规要求排除项比关闭更稳妥。WeChatAppEx是微信小程序相关进程。微信打开多个小程序、视频号、内置浏览器页面后它可能占用较高内存。处理方式退出不用的微信小程序右键任务栏微信图标退出微信再重开清理微信缓存。如果长期异常更新微信版本检查是否有小程序卡死。任务管理器结束该进程通常会自动重启治标不治本。Microsoft Edge浏览器内存占用高多数是多标签、扩展、硬件加速、休眠标签策略导致。Edge 有“休眠标签页”功能可以在设置里开启让不活跃标签释放资源。按Shift Esc打开 Edge 自带任务管理器能看到每个标签、扩展、GPU 进程的 CPU 和内存。如果某个扩展异常禁用后观察。浏览器内存占用高不一定是泄漏现代浏览器多进程架构本身就会占更多内存。SQL Server Windows NT占用大量内存是常见现象因为 SQL Server 会尽量缓存数据页。它默认会用到接近系统内存上限不代表泄漏。正确做法是设置最大服务器内存sp_configure show advanced options, 1; RECONFIGURE; sp_configure max server memory (MB), 8192; RECONFIGURE;如果服务器同时跑其他服务建议给 SQL Server 留出操作系统和其他进程的空间。比如 32GB 内存SQL Server 最大内存可设 24GB 左右具体看业务。不要看到它吃内存就杀进程数据库缓存被清空后性能会明显下降。auditd是 Linux 审计服务不是 Windows。它占用内存高通常因为审计规则太多、日志量太大、规则匹配复杂。处理方式是精简规则、轮转日志、限制审计范围。排查时用auditctl -l看规则用ausearch、aureport分析。不要直接停掉审计服务除非你清楚合规影响。3. Linux下查看CPU核数与内存/proc是真相源头3.1 lscpu、nproc、/proc/cpuinfo与CPU亲和性Linux 看 CPU核数首选lscpulscpu lscpu -e nproc getconf _NPROCESSORS_ONLN cat /proc/cpuinfo | grep -c ^processor cat /proc/cpuinfo | grep physical id | sort -u | wc -l cat /proc/cpuinfo | grep cpu cores | uniqCPU(s)是逻辑 CPU 总数Core(s) per socket是每插槽物理核Socket(s)是插槽数Thread(s) per core是每核线程数。物理核总数等于Socket(s) × Core(s) per socket逻辑核总数等于物理核乘以每核线程数。nproc显示当前进程可用的处理器数可能受亲和性影响。taskset -pc $$查看当前 shell 的 CPU 亲和性taskset -c 0,1 command可以绑定 CPU。容器里看 CPU 要特别注意。cgroup v2 查看配额cat /sys/fs/cgroup/cpu.max # 输出示例200000 100000 表示 2 个 CPU cat /sys/fs/cgroup/cpuset.cpus.effectivecgroup v1cat /sys/fs/cgroup/cpu/cpu.cfs_quota_us cat /sys/fs/cgroup/cpu/cpu.cfs_period_us cat /sys/fs/cgroup/cpuset/cpuset.cpus如果cpu.cfs_quota_us是 200000cpu.cfs_period_us是 100000可用 CPU 就是 2 个。Kubernetes 里kubectl top pod看实际使用kubectl describe pod看 limits/requests。Java 应用在容器中要关注-XX:ActiveProcessorCount避免线程池按宿主机 CPU 数创建。/proc/cpuinfo是原始信息但可读性差且虚拟机里physical id、cpu cores可能不准确。优先用lscpu。如果发现逻辑核很多但负载集中在一个核检查中断亲和性、线程绑定、单线程瓶颈。用mpstat -P ALL 1可以看每个逻辑核的使用率。3.2 free、/proc/meminfo、vmstat和MemAvailableLinux 内存查看第一命令是free -h free -m cat /proc/meminfo vmstat 1free -h输出中total是总内存used是已用free是完全空闲shared是共享内存buff/cache是缓冲和缓存available是估计可分配内存。判断内存是否紧张看available和si/so。buff/cache高不是问题内核会在需要时回收。used高也不一定有问题因为 Linux 会把空闲内存用于缓存。/proc/meminfo更详细grep -E MemTotal|MemFree|MemAvailable|Buffers|Cached|SwapTotal|SwapFree|Dirty|Writeback|Slab|SReclaimable|SUnreclaim|Committed_AS /proc/meminfo重点指标MemAvailable可分配估计Dirty待写回磁盘的数据Writeback正在写回Slab内核对象缓存SReclaimable可回收 slabSUnreclaim不可回收 slabCommitted_AS已承诺地址空间。如果SUnreclaim持续上涨可能有内核内存泄漏或驱动问题。Committed_AS远超物理内存加 Swap说明过量承诺可能触发 OOM。vmstat 1的列含义r运行队列b阻塞进程si从 Swap 换入so换出到 Swapus/sy/id/wa/st是 CPU 时间占比。si/so长期非零说明内存压力大wa高说明 I/O 等待st高说明虚拟机被宿主机偷走 CPU。sar -r 1可以看历史内存sar -W 1看换页。3.3 进程级内存top、ps、pmap、smem与cgroup看进程内存常用top -o %MEM ps aux --sort-%mem | head -20 pidstat -r -p PID 1 pmap -x PID cat /proc/PID/status | grep -E VmRSS|VmSize|RssAnon|RssFile|RssShmem|VmSwap smem -tktop的RES是常驻内存VIRT是虚拟内存SHR是共享内存。RES包含共享库多个进程会重复计算所以把所有进程 RES 相加可能超过物理内存。smem的 PSS 按共享比例分摊更接近真实占用。pmap -x PID能看到每个内存映射段适合排查大块匿名映射、mmap 文件、堆外内存。容器内存查看# cgroup v2 cat /sys/fs/cgroup/memory.current cat /sys/fs/cgroup/memory.max cat /sys/fs/cgroup/memory.stat # cgroup v1 cat /sys/fs/cgroup/memory/memory.usage_in_bytes cat /sys/fs/cgroup/memory/memory.limit_in_bytesmemory.current是当前使用memory.max是限制memory.stat里anon、file、slab、sock等能看出内存去向。Kubernetes Pod 被 OOMKilled通常是容器内存超过 limit而不是节点内存不足。检查kubectl describe pod的 Last State 和 Events结合kubectl top pod、应用堆外内存、Sidecar 占用一起看。3.4 内存泄露调试和常用检测工具Linux 内存泄漏排查工具有很多。C/C 程序常用 Valgrindvalgrind --leak-checkfull --show-leak-kindsall ./app valgrind --toolmassif ./app ms_print massif.out.pidAddressSanitizer 更轻量gcc -fsanitizeaddress -g app.c -o app ./appJava 用jmap、jcmd、MAT、ArthasGo 用pprofPython 用tracemalloc、objgraphNode.js 用--inspect和 heap snapshot。搜索词里有人写glags 调试内存泄露大概率是记混了工具名。实际排查时先确认泄漏发生在堆、堆外、内核还是文件缓存再选工具。一个实用思路不要只看瞬时值看趋势。每隔 10 秒记录一次 RSSwhile true; do date; ps -o pid,rss,vsz,cmd -p PID; sleep 10; done如果 RSS 持续上涨且业务量稳定做一次强制 GC 或重启对照。C/C 可以用malloc_stats、mallinfo2看分配器状态Java 用jcmd PID GC.heap_info、jcmd PID VM.native_memory summary。如果是内核 Slab 泄漏用slabtop、/proc/slabinfo再结合drop_caches测试可回收部分。内存检测工具方面硬件内存故障用 MemTest86、Windows 内存诊断、TestMem5、Prime95系统监控用 HWiNFO、AIDA64、CPU-ZLinux 用memtester、edac-util。如果机器频繁蓝屏、编译随机报错、文件校验失败先跑内存检测不要只怀疑软件。4. macOS、Android、嵌入式开发板换平台不换思路4.1 macOSsysctl、vm_stat、活动监视器macOS 查看 CPU核数和内存sysctl -n hw.physicalcpu sysctl -n hw.logicalcpu sysctl -n hw.memsize sysctl -n machdep.cpu.brand_string vm_stat top -l 1 | head -20hw.physicalcpu是物理核hw.logicalcpu是逻辑核hw.memsize是总内存字节数。vm_stat看页面大小、空闲、活跃、非活跃、已压缩、换出。macOS 活动监视器的“内存压力”比“已用内存”更有参考价值。绿色表示正常黄色表示有压力红色表示紧张。macOS 也会压缩内存所以“已用内存”高不一定是问题。活动监视器里“能源”页看能耗“CPU”页看线程和端口“内存”页看内存压力、已缓存文件、交换使用。如果交换持续增长说明物理内存不足。sudo purg可以清理磁盘缓存但不要频繁用系统自己会管理。开发机上 Docker、Xcode、模拟器、浏览器一起跑内存压力很容易变黄优先关模拟器、Docker Desktop 资源限制、Xcode 索引。4.2 Androidadb、dumpsys meminfo与ProfilerAndroid 查看内存adb shell cat /proc/meminfo adb shell dumpsys meminfo adb shell dumpsys meminfo com.example.app adb shell dumpsys cpuinfo adb shell top adb shell procrankdumpsys meminfo 包名会列出 PSS、Private Dirty、Heap、Stack、Graphics、Native、Code 等。PSS 按共享比例计算更适合比较应用占用。Android Studio Profiler 可以看 Java 堆、Native 内存、GPU、CPU、网络。内存泄漏常见原因静态 Context、单例持有 Activity、Handler 延迟消息、未注销监听器、匿名内部类、线程未停止。LeakCanary 能自动检测 Activity/Fragment 泄漏。Android 内存泄漏和 OOM 不是一回事。OOM 可能是单次分配过大、图片未压缩、Bitmap 过大、堆限制低泄漏是对象该回收却一直被引用。排查时先看 GC 后堆是否下降再看对象引用链。用 MAT 或 Android Studio 的 Heap Dump 分析支配树。大图用 Glide/Coil 并设置采样列表复用 ViewHolder避免在循环里创建大对象。4.3 嵌入式JZ2440和资源受限设备JZ2440 这类 ARM 开发板资源有限查看内存常用cat /proc/meminfo free busybox top cat /proc/cpuinfo嵌入式 Linux 里free可能来自 BusyBox输出格式略有差异。重点看 MemTotal、MemFree、Buffers、Cached、SwapTotal。没有 Swap 时内存耗尽直接 OOM。交叉编译时注意工具链位数、库大小、堆栈限制。ulimit -a看进程资源限制ulimit -s看栈大小。如果程序报“无法分配内存”可能是堆碎片、地址空间限制、内核 overcommit 策略不一定是总内存不够。资源受限设备还要看/proc/slabinfo、/proc/buddyinfo。buddyinfo显示各阶空闲页块如果高阶块很少大块连续内存分配会失败即使总空闲内存不少。这在摄像头、DMA、音视频解码场景很常见。解决办法包括提前预留 CMA、减少内存碎片、调整分配时机、使用内存池。5. Java、Spark、Redis、Excel等应用层怎么查内存5.1 JVM内存模型、GC与对象创建内存布局JVM 内存模型常被简化成堆和栈实际包括程序计数器、Java 虚拟机栈、本地方法栈、Java 堆、方法区/元空间、运行时常量池、直接内存。堆分新生代和老年代新生代又分 Eden、Survivor。对象创建过程包括类加载检查、分配内存、零值初始化、设置对象头、执行构造方法。对象在堆中布局通常分对象头、实例数据、对齐填充。访问定位有句柄和直接指针两种方式HotSpot 常用直接指针。查看 JVM 内存jps -l jstat -gcutil PID 1000 jmap -heap PID jcmd PID GC.heap_info jcmd PID VM.native_memory summary jstack PID stack.txtjstat -gcutil看 Eden、Old、Metaspace、YGC、FGC、GCT。频繁 Full GC 且老年代回收后不降可能内存泄漏。jmap -histo:live PID看对象直方图。MAT 分析 heap dump 找支配树和 GC Roots 引用链。GC 优化先看目标低延迟用 G1/ZGC高吞吐用 Parallel。参数不是越大越好-Xmx过大会延长 Full GC 停顿过小会频繁 GC 或 OOM。堆外内存用 NMTjava -XX:NativeMemoryTrackingdetail -jar app.jar jcmd PID VM.native_memory summary重点看 Java Heap、Class、Thread、Code、GC、Compiler、Internal、Other、Direct。Netty、gRPC、Kafka、RocketMQ、Elasticsearch 等常用直接内存。-XX:MaxDirectMemorySize限制直接内存但 JNI、mmap 不一定受它控制。如果进程 RSS 远大于堆先查 NMT再看pmap的大块匿名映射。5.2 Spark内存和容器内存Driver/Executor怎么看Spark 内存分 Driver 和 Executor。Driver 负责调度、DAG、结果收集Executor 负责执行 Task、缓存数据、Shuffle。常用参数spark-submit \ --driver-memory 4g \ --executor-memory 8g \ --executor-cores 4 \ --num-executors 10 \ --conf spark.memory.fraction0.6 \ --conf spark.memory.storageFraction0.5 \ --conf spark.memory.offHeap.enabledtrue \ --conf spark.memory.offHeap.size2g \ app.jarExecutor 内存分执行内存和存储内存二者可动态借用。缓存过多会把执行内存挤掉导致 Spill。数据倾斜会让少数 Task 处理大量数据OOM 或长尾。排查看 Spark Web UI 的 Stage、Task、Shuffle Read/Write、Spill、GC Time。序列化用 Kryo减少对象开销。大 SQL 避免collect()到 Driver改用take、write。容器里跑 Spark 还要看 cgroup 限制。YARN 或 Kubernetes 的 executor memory 包含堆、堆外、Overhead。Kubernetes 要设置spark.executor.memoryOverhead否则 Pod 可能因总内存超 limit 被 OOMKilled。堆内存加堆外内存加 overhead 不能超过容器 limit否则 Spark 自己没 OOM容器先被杀。5.3 RedisTemplate zset、XSSFWorkbook、IDEA、COMSOL、Keil等场景RedisTemplate.opsForZSet().add本身不会导致栈内存溢出。栈溢出通常是递归太深、AOP 循环调用、代理嵌套、线程栈太小。排查用jstack看线程栈找重复帧。如果业务里在 ZSet 操作外层做了递归重试或者序列化器循环引用也可能表现为栈溢出。解决办法改递归为循环限制重试次数调整-Xss要谨慎根本原因是调用深度。XSSFWorkbook内存溢出是 Apache POI 经典问题。XSSFWorkbook 把整个 Excel 加载到内存几十万行很容易 OOM。写大文件用 SXSSFWorkbook 流式写出设置滑动窗口和临时文件SXSSFWorkbook workbook new SXSSFWorkbook(100); workbook.setCompressTempFiles(true);读大文件用 SAX 事件模型或 EasyExcel。分页查询数据库避免一次性查全量。导出接口加限流、异步任务、文件下载后清理临时文件。-Xmx只能缓解不能解决架构问题。IDEA 很吃内存先调堆Help - Change Memory Settings -Xmx4096m清理缓存File - Invalidate Caches / Restart关闭不用的插件排除大目录索引关闭重复的 Maven/Gradle 守护进程。jps -l能看到 IDEA 自身和构建进程。如果idea64.exe占几 GB但系统内存充足不一定是问题。真正卡顿时看 GC 日志和线程 dump。COMSOL 4旧版本可能受 32 位地址空间限制复杂模型需要 64 位版本和足够内存。Keil 该进程已终止因为它无法分配更多的内存常见于 32 位工具链、大工程、优化等级高、链接地址空间不足。减少同时编译文件、关闭浏览器和虚拟机、增加系统页面文件、拆分工程。win软件无法分配到更多内存先确认是不是 32 位进程32 位进程用户地址空间通常只有 2GB 或 4GB和物理内存大小无关。6. 硬件视角内存频率、时序、超频、分配器与内存池6.1 3200频率验证、Ryzen时序计算、ITX超频原因确认 3200 频率内存是否跑满Get-CimInstance Win32_PhysicalMemory | Select-Object Capacity, Speed, ConfiguredClockSpeed, PartNumbersudo dmidecode -t memory | grep -E Size|Speed|Configured Memory Speed|Locator sudo lshw -short -C memory如果ConfiguredClockSpeed只有 2133/2400/2666进 BIOS 开启 XMP、DOCP 或 EXPO。开启后可能不稳定需要跑 TestMem5、MemTest86。频率不是唯一指标时序和电压同样重要。DDR 内存标称 3200实际时钟 1600MHz因为双倍数据速率。延迟纳秒计算公式延迟(ns) 时序值 / 实际时钟(MHz) × 1000 CL16 在 DDR4-3200 下16 / 1600 × 1000 10ns CL18 在 DDR4-3600 下18 / 1800 × 1000 10nsRyzen 平台关注 FCLK、UCLK、MCLK。DDR4-3200 时 MCLK 1600MHzUCLK 1600MHzFCLK 1600MHz1:1 模式延迟较低。DDR4-3600 时 MCLK 1800FCLK 1800 比较理想如果 FCLK 上不去分频后延迟可能增加。ITX 主板内存超频更强常见原因是只有两个内存插槽走线短、拓扑简单、信号完整性好干扰少但 ITX 供电和散热空间小CPU 超频未必强。大内存架构还要考虑通道数、NUMA、ECC、内存交错。内存检测工具推荐MemTest86 做硬件级检测TestMem5 做超频稳定性Prime95 Large FFT 看内存控制器压力HWiNFO 看频率、时序、温度、错误计数。超频后如果出现 WHEA 错误、蓝屏、编译随机失败先降频或放宽时序不要只加电压。6.2 内存分配器、内存池、C/C union、strstr二进制、内存映射内存分配器是用户态管理堆内存的组件。Linux glibc 默认 ptmalloc多线程高并发下可能有锁竞争和碎片jemalloc、tcmalloc 在多线程场景表现更好Redis、MySQL、Nginx、Rust 生态常集成。查看进程使用哪个分配器lsof -p PID | grep -E libc|jemalloc|tcmalloc内存池是提前申请一大块内存自己管理分配释放减少系统调用和碎片。适合固定大小对象、网络包、数据库页、游戏引擎。C 可以用对象池、slab、arena。注意内存池不是万能的生命周期错配会导致更严重泄漏。C/C 中union的所有成员共享同一块内存大小等于最大成员并按最大对齐要求对齐。可以用 union 查看浮点数的二进制表示但要注意字节序和严格别名规则。strstr()用于查找以\0结尾的字符串不能可靠查找二进制内存因为二进制数据可能包含\0。查找二进制内存要用memmem()或自己实现基于长度和memcmp的搜索。内存映射mmap把文件或匿名内存映射到进程地址空间减少用户态和内核态拷贝。数据库、消息队列、日志库常用 mmap 加速读写。匿名 mmap 可用于大块内存分配malloc大块内存时底层也可能用 mmap。查看映射用pmap -x PID、cat /proc/PID/maps。如果发现大量 64MB 匿名映射可能是 glibc arena也可能是程序自己 mmap。6.3 大内存架构与NUMA、物理内存分配大内存服务器常见 NUMA 架构每个 CPU 插槽有本地内存跨插槽访问延迟更高。Linux 用numactl --hardware、lscpu | grep NUMA查看。数据库、Java、Redis 绑核时要注意内存本地性。numactl --cpunodebind0 --membind0 command让进程用节点 0 的 CPU 和内存。Kubernetes 有 Topology Manager、CPU Manager、Memory Manager但配置复杂普通业务别盲目开。物理内存分配不是“有空闲内存就能分配”。内核有 overcommit 策略、伙伴系统、内存碎片、cgroup 限制、地址空间限制。/proc/buddyinfo看连续页块/proc/pagetypeinfo看迁移类型。大页 HugePages 能减少 TLB miss但需要预留配置不当会浪费内存。透明大页 THP 在某些数据库上可能造成延迟抖动需要评估后关闭或改为 madvise。排查“无法分配更多内存”时按顺序问进程是 32 位还是 64 位cgroup 限制多少ulimit -v限制了吗内核 overcommit 策略是什么有没有连续大块内存Swap 是否可用页大小和大页配置这些问题比单纯看任务管理器百分比更接近真相。7. 常见问题速查与排查技巧实录7.1 Windows和桌面端问题速查表现象优先查看常见处理任务管理器内存 60% 但很卡磁盘响应、CPU 队列、页面文件、内存压缩资源监视器看磁盘队列关闭高 I/O 进程检查 SSD 健康antimalware service executable 占内存Defender 扫描计划、排除项添加开发目录排除项错开全盘扫描更新病毒库WeChatAppEx 占用内存过高微信小程序、视频号、缓存退出小程序重启微信清理缓存更新版本Edge 浏览器内存占用高标签、扩展、GPU 进程ShiftEsc 看 Edge 任务管理器开休眠标签禁用异常扩展Win10 关闭内存压缩Get-MMAgent管理员运行Disable-MMAgent -mc后重启非必要不关为硬件保留的内存很高集成显卡、BIOS、驱动检查显存设置、BIOS 内存重映射、更新芯片组驱动怎么清理电脑 C 盘内存磁盘空间不是内存磁盘清理、存储感知、迁移页面文件、清理编译缓存用户拒绝访问内存文件权限管理员权限、ACL、安全软件以管理员运行工具检查文件所有权联系安全管理员资源管理器内存泄漏explorer.exe 提交和工作集趋势重启资源管理器卸载异常右键菜单和外壳扩展32 位软件无法分配更多内存进程位数、地址空间换 64 位版本拆分任务增加页面文件只能缓解Windows 桌面端排查顺序建议任务管理器性能页看 CPU、内存、磁盘、GPU资源监视器看具体进程性能监视器看历史曲线事件查看器看 WHEA、磁盘、应用错误。如果内存占用高但可用内存充足不要急着杀进程。先看硬错误、页面文件、压缩内存、提交量。如果提交量接近上限才需要处理页面文件或内存泄漏。7.2 Linux和服务端问题速查表现象优先查看常见处理free 显示可用内存很少available、si/sobuff/cache 可回收持续换页才危险容器 OOMKilledmemory.current、memory.max、memory.stat调整 limit查堆外内存设置 JVM 容器参数Java 进程 RSS 大于 XmxNMT、pmap、直接内存jcmd VM.native_memory限制直接内存查 Netty/mmapSQL Server Windows NT 占内存max server memory设置合理上限不要直接杀进程auditd 占用内存高审计规则、日志量精简规则轮转日志限制审计范围内存泄露RSS 趋势、Valgrind、ASan、MAT做趋势记录GC 后对比分析引用链内核 Slab 持续上涨slabtop、/proc/slabinfo查驱动、文件系统、内核模块必要时重启验证大块内存分配失败/proc/buddyinfo、碎片预留 CMA、大页、内存池减少碎片3200 内存没跑满dmidecode、lshwBIOS 开 XMP/EXPO验证 Configured SpeedGPU CPU 内存不高但卡I/O、DPC、锁、网络iostat、pidstat、perf、LatencyMon 分层排查Linux 服务端排查内存我的习惯是三层同时看第一层free -h和/proc/meminfo判断全局压力第二层top、ps、smem找进程第三层pmap、NMT、/proc/PID/smaps找具体映射。容器再加 cgroup。只看一层很容易误判。比如宿主机available充足但容器 limit 很小容器内 OOM或者进程 RSS 高但大部分是共享库真正私有内存不大。7.3 应用层与开发工具问题速查表场景关键点处理建议JVM 栈内存溢出递归、AOP 循环、线程栈jstack看重复帧改递归限制重试慎调-XssRedisTemplate zset add 栈溢出序列化循环、代理嵌套检查调用链改循环排查切面XSSFWorkbook 内存溢出全量 DOM 加载SXSSFWorkbook、EasyExcel、分页、异步导出Spark OOMExecutor 内存、倾斜、Shuffle调 executor memoryKryo处理倾斜加 overheadIDEA 吃内存索引、插件、构建进程调-Xmx清缓存关插件排除目录Android 内存泄漏Context、Handler、监听器LeakCanaryHeap Dump弱引用注销监听COMSOL 4 内存不足32 位限制、模型规模换 64 位简化网格增加物理内存Keil 无法分配更多内存32 位工具链、大工程拆分工程关优化增加页面文件换 64 位工具内存溢出和内存截取区别溢出是分配失败截取是数据截断前者查容量和泄漏后者查长度和拷贝边界内存取证Netscan、Volatility 2 GUI合法应急响应中采集镜像分析进程和网络注意证据链应用层内存问题先分清是堆内、堆外、栈、直接内存、还是内核缓存。Java 堆内问题用 MAT堆外用 NMT栈用 jstack直接内存用 NMT 和 pmap。Spark 看 Web UI 和 executor 日志。Excel 导出看是否流式。Android 看 Profiler 和 LeakCanary。工具很多但判断路径基本一致现象、趋势、映射、引用链、复现。7.4 我自己的排查顺序和避坑心得实际排查时我会按这个顺序走先确认用户说的“内存高”到底是任务管理器百分比、free的 used、JVM 堆、还是容器 limit再确认是持续上涨还是瞬时高峰然后看趋势至少记录 5 到 10 分钟接着分层到进程、线程、映射、对象最后才动参数。直接重启、直接加内存、直接调大-Xmx可能暂时缓解但会把根因藏得更深。几个反复踩过的坑第一Linux 的 buff/cache 不是泄漏别看到 cached 高就清理第二Windows 任务管理器“使用中”包含压缩内存关闭内存压缩前先想清楚目的第三JVM RSS 大于 Xmx 很正常堆外、元空间、线程栈、代码缓存、JNI、mmap 都算 RSS第四容器里看宿主机 CPU核数和内存会误判必须看 cgroup第五32 位程序有地址空间天花板物理内存再大也没用第六内存超频后不稳定比不超频更可怕编译随机报错、文件损坏、蓝屏都可能是内存错误。最后再分享一个我常用的小技巧给关键进程做一张“内存基线表”。记录正常业务量下的 RSS、堆使用、线程数、available、si/so、cgroup 使用量。出问题时不用靠感觉直接和基线对比。趋势比绝对值重要可复现比猜测重要。CPU核数和内存使用情况这两个数字表面简单背后是操作系统、硬件、运行时、容器四层账本。把这四层账本对齐大多数“机器卡、内存涨、进程被杀”的问题都能找到入口。
返回列表