mirror of
https://github.com/Baim0/baim0-learn.git
synced 2026-10-19 17:57:45 +08:00
vault backup: 2024-05-25 19:26:16
This commit is contained in:
Vendored
+6
-5
@@ -13,7 +13,7 @@
|
||||
"state": {
|
||||
"type": "markdown",
|
||||
"state": {
|
||||
"file": "k8sFunny/未命名.md",
|
||||
"file": "k8sFunny/06 Linux 内存分析与观测.md",
|
||||
"mode": "source",
|
||||
"source": false
|
||||
}
|
||||
@@ -85,7 +85,7 @@
|
||||
"state": {
|
||||
"type": "backlink",
|
||||
"state": {
|
||||
"file": "k8sFunny/未命名.md",
|
||||
"file": "k8sFunny/06 Linux 内存分析与观测.md",
|
||||
"collapseAll": false,
|
||||
"extraContext": false,
|
||||
"sortOrder": "alphabetical",
|
||||
@@ -102,7 +102,7 @@
|
||||
"state": {
|
||||
"type": "outgoing-link",
|
||||
"state": {
|
||||
"file": "k8sFunny/未命名.md",
|
||||
"file": "k8sFunny/06 Linux 内存分析与观测.md",
|
||||
"linksCollapsed": false,
|
||||
"unlinkedCollapsed": true
|
||||
}
|
||||
@@ -125,7 +125,7 @@
|
||||
"state": {
|
||||
"type": "outline",
|
||||
"state": {
|
||||
"file": "k8sFunny/未命名.md"
|
||||
"file": "k8sFunny/06 Linux 内存分析与观测.md"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -149,8 +149,9 @@
|
||||
},
|
||||
"active": "abf2c3acea123adf",
|
||||
"lastOpenFiles": [
|
||||
"k8sFunny/07 coredns 常见问题.md",
|
||||
"k8sFunny/06 Linux 内存分析与观测.md",
|
||||
"prometheus/04 服务发现.md",
|
||||
"k8sFunny/未命名.md",
|
||||
"terrafrom",
|
||||
"prometheus/03 查询.md",
|
||||
"prometheus/01 基本概念.md",
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
# Linux 内存分析与观测
|
||||
|
||||
## 一、Linux 内存的观测
|
||||
## Linux 内存的观测
|
||||
|
||||
### 1. 系统内存
|
||||
|
||||
@@ -102,11 +100,10 @@ $ vmstat 2
|
||||
|PSS|Proportional Set Size|比例集大小,与RSS类似,唯⼀的区别是在计算共享库内存是是按⽐例分配。⽐如某个共享库占用了3M内存,同时被3个进程共享,那么在计算这3个进程的PSS时,该共享库这会贡献1兆内存。PSS通过考虑共享内存的比例,对共享内存进行分配,从而更准确地反映了进程占用的实际物理内存。|
|
||||
|USS|Unique Set Size|唯一集大小,代表进程独占的物理内存大小,不包含Swap和共享库。也就是说,如果有多个进程共享同一块内存,这部分内存只会计入一个进程的USS。|
|
||||
|
||||

|
||||
|
||||
以上这些指标可以通过`/proc/[pid]/smaps`文件中找到,其中pid是进程的ID。这四者的⼤⼩关系是:VSS >= RSS >= PSS >= USS
|
||||
|
||||
分析进程内存的整体使⽤情况,可以从 `/proc/[pid]/status` 文件中读取信息,显示的内容包括进程的VSS和RSS,下面是相关字段的含义:
|
||||
|
||||
```shell
|
||||
# 系统中 kubelet进程为例
|
||||
$ cat /proc/377598/status
|
||||
@@ -555,7 +552,6 @@ nginx-ingress-controller-75c587dfd5-vwmpz 4m 121Mi
|
||||
```
|
||||
|
||||
需要注意的是 Metrics-Server 并不是 kube-apiserver 的一部分,而是通过 Aggregator 这种插件机制,在独立部署的情况下同 kube-apiserver 一起统一对外服务的。
|
||||
|
||||
当 Kubernetes 的 API Server 开启了 Aggregator 模式之后,再访问 `/apis/metrics.k8s.io/v1beta1` 的时候,实际上访问到的是一个叫作 kube-aggregator 的代理。而 kube-apiserver,正是这个代理的一个后端, Metrics-Server 则是另一个后端。
|
||||
|
||||
通过上面的接口访问Metrics-Server服务,最后会获取到由 cAdvisor 提供的 cgroup数据,这里对应的内存指标名为 container_memory_working_set_bytes。其代表的含义是容器真实使用的内存量,也是资源限制limit时的重启判断依据,超过limit会导致oom。
|
||||
@@ -654,467 +650,8 @@ container_memory_usage_bytes指标可以直接从 cgroup 中的 memory.usage_in_
|
||||
}
|
||||
```
|
||||
|
||||
## 二、案例分析
|
||||
|
||||
### 1. 低版本内核系统中容器内存 total_active_file 数量级高
|
||||
- 问题描述
|
||||
- 某集群中的Pod,监控显示 Pod内存使用为 5.5G,但是 Pod中服务本身只使用了1.6G
|
||||
|
||||
- 重点排查步骤
|
||||
- kubernetes 官方文档也说明了 kubelet 将 active_file 内存区域视为不可回收
|
||||
- [active_file 内存未被视为可用内存](https://kubernetes.io/zh-cn/docs/concepts/scheduling-eviction/node-pressure-eviction/#active-file-%E5%86%85%E5%AD%98%E6%9C%AA%E8%A2%AB%E8%A7%86%E4%B8%BA%E5%8F%AF%E7%94%A8%E5%86%85%E5%AD%98)
|
||||
```shell
|
||||
# 节点系统 centos_7_06_64_20G_alibase_20190619.vhd 3.10.0-957.21.3.el7.x86_64
|
||||
# /sys/fs/cgroup/memory/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod<pod ID>.slice/
|
||||
$ cat /sys/fs/cgroup/memory/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod9c785710_32ec_4baa_a967_3183025768fa.slice/memory.stat
|
||||
cache 0
|
||||
rss 0
|
||||
rss_huge 0
|
||||
mapped_file 0
|
||||
swap 0
|
||||
pgpgin 0
|
||||
pgpgout 0
|
||||
pgfault 0
|
||||
pgmajfault 0
|
||||
inactive_anon 0
|
||||
active_anon 0
|
||||
inactive_file 0
|
||||
active_file 0
|
||||
unevictable 0
|
||||
hierarchical_memory_limit 3221225472
|
||||
hierarchical_memsw_limit 9223372036854771712
|
||||
total_cache 1928810496
|
||||
total_rss 1292386304
|
||||
total_rss_huge 14680064
|
||||
total_mapped_file 32768
|
||||
total_swap 0
|
||||
total_pgpgin 4825542
|
||||
total_pgpgout 4042694
|
||||
total_pgfault 3627216
|
||||
total_pgmajfault 262
|
||||
total_inactive_anon 0
|
||||
total_active_anon 1292386304
|
||||
total_inactive_file 580984832
|
||||
total_active_file 1347825664 # 数量级很高
|
||||
total_unevictable 0
|
||||
|
||||
# 集群中每个node节点上容器的total_active_file缓存,占用都有20-30G
|
||||
$ cat /sys/fs/cgroup/memory/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod*.slice/memory.stat |grep total_active_file |awk '{a+=$2}END{print a}'
|
||||
35410173952
|
||||
|
||||
# 高内核版本的系统 ALinux2 4.19.91-27.7.al7.x86_64 内核
|
||||
$ cat /sys/fs/cgroup/memory/kubepods/burstable/poda3726f29-0381-11ee-a39b-5254001450b7/memory.stat
|
||||
cache 0
|
||||
rss 0
|
||||
rss_huge 0
|
||||
shmem 0
|
||||
mapped_file 0
|
||||
dirty 0
|
||||
writeback 0
|
||||
swap 0
|
||||
pgpgin 0
|
||||
pgpgout 0
|
||||
pgfault 0
|
||||
pgmajfault 0
|
||||
inactive_anon 0
|
||||
active_anon 0
|
||||
inactive_file 0
|
||||
active_file 0
|
||||
unevictable 0
|
||||
hierarchical_memory_limit 4294967296
|
||||
hierarchical_memsw_limit 9223372036854771712
|
||||
total_cache 1852940288
|
||||
total_rss 2250285056
|
||||
total_rss_huge 0
|
||||
total_shmem 0
|
||||
total_mapped_file 401408
|
||||
total_dirty 520192
|
||||
total_writeback 0
|
||||
total_swap 0
|
||||
total_pgpgin 53762041
|
||||
total_pgpgout 52760277
|
||||
total_pgfault 11727726
|
||||
total_pgmajfault 18
|
||||
total_inactive_anon 0
|
||||
total_active_anon 2250285056
|
||||
total_inactive_file 1812811776
|
||||
total_active_file 40095744 # 明显下降
|
||||
total_unevictable 0
|
||||
```
|
||||
|
||||
- 验证测试
|
||||
- 清理缓存 `echo 1 > /proc/sys/vm/drop_caches`,有效
|
||||
- 升级node节点的系统内核小版本,进行验证,没有效果
|
||||
```shell
|
||||
# 升级内核版本
|
||||
# 1、确定当前内核版本
|
||||
$ uname -r
|
||||
3.10.0-514.26.2.el7.x86_64 # 需要升级到 kernel-3.10.0-1160.45.1.el7.x86_64.rpm 版本(用于安装GPU驱动)
|
||||
|
||||
# 2、下载指定内核版本
|
||||
# 下载地址:http://ftp.scientificlinux.org/linux/scientific/7.0/x86_64/updates/security/
|
||||
$ wget http://ftp.scientificlinux.org/linux/scientific/7.0/x86_64/updates/security/kernel-3.10.0-1160.45.1.el7.x86_64.rpm
|
||||
|
||||
# 3、安装内核版本
|
||||
# 直接使用yum安装下载到本地的内核版本
|
||||
$ yum -y install kernel-3.10.0-1160.45.1.el7.x86_64.rpm # 安装过程中没有报错,表示安装成功
|
||||
|
||||
# 4、列出当前系统所有内核版本
|
||||
$ awk -F\' '$1=="menuentry " {print i++ " : " $2}' $(find /boot -name grub.cfg)
|
||||
|
||||
# 5、列出系统引导入口
|
||||
$ grub2-editenv list
|
||||
|
||||
# 6、修改默认引导入口
|
||||
$ grub2-set-default 0
|
||||
|
||||
# 7、重新构建一下grub菜单配置文件
|
||||
$ grub2-mkconfig -o /boot/grub2/grub.cfg
|
||||
|
||||
# 验证 total_active_file 正常
|
||||
$ cat memory.stat |grep total_active_file
|
||||
total_active_file 61095936 # 61095936/1024/1024/1024 = 0.056GB
|
||||
```
|
||||
|
||||
- 临时方案
|
||||
- 先执行 `sync` ,用于将文件系统的缓冲区数据立即写入磁盘,以确保数据的持久性。
|
||||
- 然后再执行下面的命令
|
||||
- 清除pagecache `echo 1 > /proc/sys/vm/drop_caches`
|
||||
- 回收slab分配器 `echo 2 > /proc/sys/vm/drop_caches`
|
||||
- 执行 `sync` 会把内存数据落盘;回收slab分配器会释放页缓存,回收期间会对系统性能产生一些影响,但影响不大
|
||||
|
||||
- 最终方案
|
||||
- 集群新增使用高内核版本系统的节点
|
||||
- 服务迁移,逐渐替换老节点
|
||||
|
||||
### 2. java容器内存过大
|
||||
- 问题描述
|
||||
- 某生产集群中的Java Pod,监控显示 Jvm只使用了 1.3G,但是Pod内存监控却有 4.88G
|
||||
|
||||
- 信息收集
|
||||
```shell
|
||||
$ kubectl -n app-ns get pod app-java-86348df32-rs8ne -oyaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: app-86348df32-rs8ne
|
||||
namespace: app-ns
|
||||
spec:
|
||||
containers:
|
||||
- env:
|
||||
- name: JAVA_OPTS
|
||||
value: java -jar -XX:+UseContainerSupport -XX:InitialRAMPercentage=70.0 -XX:MaxRAMPercentage=70.0
|
||||
-Dspring.profiles.active=prod -XX:-UseAdaptiveSizePolicy -XX:NewRatio=2 -XX:SurvivorRatio=8 app.jar
|
||||
- name: K8S_ENV
|
||||
value: prod
|
||||
- name: k8s_logs_console
|
||||
value: stdout
|
||||
- name: k8s_logs_console_tags
|
||||
value: topic=k8s-app_java-console-log
|
||||
image: hub/app-java:0.0.2-v9926675b
|
||||
imagePullPolicy: Always
|
||||
lifecycle:
|
||||
preStop:
|
||||
exec:
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- echo stop > /tmp/prestop;wget http://127.0.0.1:54199/offline 2>/tmp/null;sleep
|
||||
30;exit 0
|
||||
livenessProbe:
|
||||
failureThreshold: 10
|
||||
httpGet:
|
||||
path: /app-java/keepalive
|
||||
port: http
|
||||
scheme: HTTP
|
||||
periodSeconds: 10
|
||||
successThreshold: 1
|
||||
timeoutSeconds: 5
|
||||
name: app-java
|
||||
ports:
|
||||
- containerPort: 11192
|
||||
name: http
|
||||
protocol: TCP
|
||||
readinessProbe:
|
||||
failureThreshold: 10
|
||||
httpGet:
|
||||
path: /app-java/keepalive
|
||||
port: http
|
||||
scheme: HTTP
|
||||
periodSeconds: 10
|
||||
successThreshold: 1
|
||||
timeoutSeconds: 5
|
||||
resources:
|
||||
limits:
|
||||
cpu: 2048m
|
||||
memory: 6Gi
|
||||
requests:
|
||||
cpu: 200m
|
||||
memory: 2Gi
|
||||
...
|
||||
|
||||
$ kubectl -n app-ns top pod app-java-86348df32-rs8ne
|
||||
NAME CPU(cores) MEMORY(bytes)
|
||||
app-java-86348df32-rs8ne 229m 5006Mi
|
||||
|
||||
# 以下为java容器内的执行结果
|
||||
$ cat /sys/fs/cgroup/memory/memory.stat
|
||||
cache 1146900480
|
||||
rss 5227409408
|
||||
rss_huge 1799356416
|
||||
shmem 0
|
||||
mapped_file 3784704
|
||||
dirty 0
|
||||
writeback 0
|
||||
swap 0
|
||||
workingset_refault_anon 0
|
||||
workingset_refault_file 67313664
|
||||
workingset_activate_anon 0
|
||||
workingset_activate_file 36360192
|
||||
workingset_restore_anon 0
|
||||
workingset_restore_file 0
|
||||
workingset_nodereclaim 0
|
||||
pgpgin 11128953
|
||||
pgpgout 10215508
|
||||
pgfault 89880285
|
||||
pgmajfault 66
|
||||
inactive_anon 5226868736
|
||||
active_anon 0
|
||||
inactive_file 1128783872
|
||||
active_file 18247680
|
||||
unevictable 0
|
||||
hierarchical_memory_limit 6442450944
|
||||
hierarchical_memsw_limit 6442450944
|
||||
total_cache 1146900480
|
||||
total_rss 5227409408
|
||||
total_rss_huge 1799356416
|
||||
total_shmem 0
|
||||
total_mapped_file 3784704
|
||||
total_dirty 0
|
||||
total_writeback 0
|
||||
total_swap 0
|
||||
total_workingset_refault_anon 0
|
||||
total_workingset_refault_file 67313664
|
||||
total_workingset_activate_anon 0
|
||||
total_workingset_activate_file 36360192
|
||||
total_workingset_restore_anon 0
|
||||
total_workingset_restore_file 0
|
||||
total_workingset_nodereclaim 0
|
||||
total_pgpgin 11128953
|
||||
total_pgpgout 10215508
|
||||
total_pgfault 89880285
|
||||
total_pgmajfault 66
|
||||
total_inactive_anon 5226868736
|
||||
total_active_anon 0
|
||||
total_inactive_file 1128783872
|
||||
total_active_file 18247680
|
||||
total_unevictable 0
|
||||
|
||||
$ ps -ef
|
||||
UID PID PPID C STIME TTY TIME CMD
|
||||
root 1 0 0 Aug08 ? 00:00:00 /pause
|
||||
root 64 0 0 Aug08 ? 00:00:00 /bin/sh -c ${JAVA_OPTS}
|
||||
root 69 64 23 Aug08 ? 19:31:54 java -jar -XX:+UseContainerSupport -XX:InitialRAMPercentage=70.0 -XX:MaxRAMPercentage=70.0 -Dspring.profiles.active=prod -XX:-UseAdaptiveSizePolicy -XX:NewRatio=2
|
||||
root 145074 0 0 Aug11 pts/0 00:00:00 /bin/sh -c TERM=xterm-256color; export TERM; LANG=C.UTF-8; export LANG; [ -x /bin/bash ] && ([ -x /usr/bin/script ] && /usr/bin/script -q -c "/bin/bash" /dev/null
|
||||
root 145079 145074 0 Aug11 pts/0 00:00:00 /bin/sh -c TERM=xterm-256color; export TERM; LANG=C.UTF-8; export LANG; [ -x /bin/bash ] && ([ -x /usr/bin/script ] && /usr/bin/script -q -c "/bin/bash" /dev/null
|
||||
root 145080 145079 0 Aug11 pts/0 00:00:00 /usr/bin/script -q -c /bin/bash /dev/null
|
||||
root 145081 145080 0 Aug11 pts/1 00:00:00 sh -c /bin/bash
|
||||
root 145082 145081 0 Aug11 pts/1 00:00:00 /bin/bash
|
||||
root 145251 145082 0 Aug11 pts/1 00:00:17 top
|
||||
root 189604 0 0 11:03 pts/2 00:00:00 /bin/sh -c TERM=xterm-256color; export TERM; [ -x /bin/bash ] && ([ -x /usr/bin/script ] && /usr/bin/script -q -c "/bin/bash" /dev/null || exec /bin/bash) || exec
|
||||
root 189609 189604 0 11:03 pts/2 00:00:00 /bin/sh -c TERM=xterm-256color; export TERM; [ -x /bin/bash ] && ([ -x /usr/bin/script ] && /usr/bin/script -q -c "/bin/bash" /dev/null || exec /bin/bash) || exec
|
||||
root 189610 189609 0 11:03 pts/2 00:00:00 /usr/bin/script -q -c /bin/bash /dev/null
|
||||
root 189611 189610 0 11:03 pts/3 00:00:00 sh -c /bin/bash
|
||||
root 189612 189611 0 11:03 pts/3 00:00:00 /bin/bash
|
||||
root 189696 0 0 11:05 pts/4 00:00:00 /bin/sh -c TERM=xterm-256color; export TERM; [ -x /bin/bash ] && ([ -x /usr/bin/script ] && /usr/bin/script -q -c "/bin/bash" /dev/null || exec /bin/bash) || exec
|
||||
root 189701 189696 0 11:05 pts/4 00:00:00 /bin/sh -c TERM=xterm-256color; export TERM; [ -x /bin/bash ] && ([ -x /usr/bin/script ] && /usr/bin/script -q -c "/bin/bash" /dev/null || exec /bin/bash) || exec
|
||||
root 189702 189701 0 11:05 pts/4 00:00:00 /usr/bin/script -q -c /bin/bash /dev/null
|
||||
root 189703 189702 0 11:05 pts/5 00:00:00 sh -c /bin/bash
|
||||
root 189704 189703 0 11:05 pts/5 00:00:00 /bin/bash
|
||||
root 189831 189704 0 11:08 pts/5 00:00:00 ps -ef
|
||||
|
||||
$ cat /proc/69/status
|
||||
Name: java
|
||||
Umask: 0022
|
||||
State: S (sleeping)
|
||||
Tgid: 69
|
||||
Ngid: 0
|
||||
Pid: 69
|
||||
PPid: 64
|
||||
TracerPid: 0
|
||||
Uid: 0 0 0 0
|
||||
Gid: 0 0 0 0
|
||||
FDSize: 1024
|
||||
Groups:
|
||||
NStgid: 69
|
||||
NSpid: 69
|
||||
NSpgid: 64
|
||||
NSsid: 64
|
||||
VmPeak: 21901288 kB
|
||||
VmSize: 21835752 kB
|
||||
VmLck: 0 kB
|
||||
VmPin: 0 kB
|
||||
VmHWM: 5127832 kB
|
||||
VmRSS: 5120108 kB
|
||||
RssAnon: 5095276 kB
|
||||
RssFile: 24832 kB
|
||||
RssShmem: 0 kB
|
||||
VmData: 5758564 kB
|
||||
VmStk: 168 kB
|
||||
VmExe: 4 kB
|
||||
VmLib: 17776 kB
|
||||
VmPTE: 11688 kB
|
||||
VmSwap: 0 kB
|
||||
HugetlbPages: 0 kB
|
||||
CoreDumping: 0
|
||||
THP_enabled: 1
|
||||
Threads: 225
|
||||
SigQ: 0/506295
|
||||
SigPnd: 0000000000000000
|
||||
ShdPnd: 0000000000000000
|
||||
SigBlk: 0000000000000000
|
||||
SigIgn: 0000000000000000
|
||||
SigCgt: 2000000191005cdf
|
||||
CapInh: 00000000a80425fb
|
||||
CapPrm: 00000000a80425fb
|
||||
CapEff: 00000000a80425fb
|
||||
CapBnd: 00000000a80425fb
|
||||
CapAmb: 0000000000000000
|
||||
NoNewPrivs: 0
|
||||
Seccomp: 0
|
||||
Speculation_Store_Bypass: vulnerable
|
||||
Cpus_allowed: ffffffff
|
||||
Cpus_allowed_list: 0-31
|
||||
Mems_allowed: 00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000001
|
||||
Mems_allowed_list: 0
|
||||
voluntary_ctxt_switches: 5
|
||||
nonvoluntary_ctxt_switches: 5
|
||||
```
|
||||
|
||||

|
||||
|
||||
- 数据分析
|
||||
```
|
||||
从之前收集的信息看:
|
||||
|
||||
1、Java容器内存
|
||||
container_memory_usage_bytes = container_memory_rss + container_memory_cache + kernel memory(kernel可以忽略)
|
||||
container_memory_working_set_bytes = container_memory_usage_bytes - total_inactive_file(未激活的匿名缓存页)
|
||||
= container_memory_rss + container_memory_cache + kernel memory(kernel可以忽略) - total_inactive_file
|
||||
= 5227409408 + 1146900480 - 1128783872
|
||||
= 5245526061/1024/1024/1024
|
||||
= 4.885 GB
|
||||
其中 container_memory_cache - total_inactive_file(未激活的匿名缓存页)= 1146900480 - 1128783872
|
||||
total_inactive_file 本身就包含在cache里面,所以这里的4.88GB中,包含的cache可以忽略不计了
|
||||
|
||||
2、Java信息
|
||||
Jvm堆栈内存为 1.3GB
|
||||
Java启动参数为
|
||||
java -jar -XX:+UseContainerSupport -XX:InitialRAMPercentage=70.0 -XX:MaxRAMPercentage=70.0
|
||||
-Dspring.profiles.active=prod -XX:-UseAdaptiveSizePolicy -XX:NewRatio=2 -XX:SurvivorRatio=8 app.jar
|
||||
|
||||
3、Java进程信息
|
||||
pod中 cat /proc/69/status 这个java进程的信息
|
||||
Name: java
|
||||
VmRSS: 5120108 kB/1024 = 5000mB /1024 = 4.88gB
|
||||
|
||||
4、服务pod资源限制为
|
||||
resources:
|
||||
limits:
|
||||
cpu: 2048m
|
||||
memory: 6Gi
|
||||
requests:
|
||||
cpu: 200m
|
||||
memory: 2Gi
|
||||
```
|
||||
|
||||

|
||||
|
||||
- 最终结论
|
||||
- 根据计算得出,Java容器内存为 4.885GB,cache可以忽略不计
|
||||
- Java容器内java进程,VmRSS 为 4.88gB,说明该java进程的内存使用量基本占用了,Java容器全部的已使用内存
|
||||
- 而此时jvm只有1.3GB,需要根据代码分析,其余内存是如何消耗的,是否有泄漏现象
|
||||
|
||||
### 3. java容器进程占用问题
|
||||
|
||||
#### 问题描述
|
||||
- 场景一
|
||||
- 用户容器内的java进程,使用了约 5.3g的内存
|
||||
- 但是从Java的应用监控看,堆内存4454M,非堆内存426M
|
||||
- 这两个加起来还差 5.3g-4.8g=0.5g 左右
|
||||
- 下图,是通过 `jstat -class {pid}` 拿的数据
|
||||
|
||||
- 场景二
|
||||
- 场景一的pod,第二天凌晨出现了 fullgc,Java的jvm堆栈监控显示内存下降 2G左右,但是容器中的java进程内存消耗还是约 5.3g
|
||||
- 即 Java进程实际使用的物理内存,没有释放。查看内核问题,实际也是这样。
|
||||
|
||||
- `jmap`
|
||||
- 使用`jmap -heap pid`输出堆内存中的对象信息,包括产生了哪些对象,对象数量多少等。
|
||||
- 查看堆内存初始化配置信息以及堆内存的使用情况
|
||||
- 使用`jmap -histo[:live] pid`查看堆内存中的对象数目、大小统计直方图,如果带上 live 则只统计活对象
|
||||
|
||||
- 排查过程
|
||||
- 可以使用 pmap 命令
|
||||
- 关于 pmap 命令的介绍,可以参考这个文档 [pmap,linux工具pmap原理?](https://www.cnblogs.com/Chary/p/16719738.html)
|
||||
- 下面是基于 `pmap -x 1` 命令,打印的容器 java主进程的内存消耗
|
||||
- 可以看到rss总数是可以对上的,而第一个内存地址 00000006a0000000 有 4502168,即 4.29GB,看起来就是jvm的占用
|
||||
```shell
|
||||
➜ pmap -x 1
|
||||
1: java -jar -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/logs/${HOSTNAME}-dump.log -XX:NewRatio=2 -XX:InitialRAMPercentage=75.0 -XX:MinRAMPercentage=75.0 -XX:MaxRAMPercentage=75.0 -Xss512K -XX:+UseConcMarkSweepGC -Dspring.profiles.active=pro -Darthas.telnetPort=3659 -Darthas.httpPort=8564 -Darthas.agent-id=sch-inventory-start demp-xxxxx.jar
|
||||
Address Kbytes RSS Dirty Mode Mapping
|
||||
00000006a0000000 4750420 4502168 4502168 rw--- [ anon ]
|
||||
00000007c1f15000 1016748 0 0 ----- [ anon ]
|
||||
0000561e5793b000 4 4 0 r-x-- java
|
||||
0000561e57b3b000 4 4 4 r---- java
|
||||
0000561e57b3c000 4 4 4 rw--- java
|
||||
0000561e58949000 4156 4136 4136 rw--- [ anon ]
|
||||
00007fb12b6a2000 12 0 0 ----- [ anon ]
|
||||
00007fb12b6a5000 504 24 24 rw--- [ anon ]
|
||||
00007fb12b723000 12 0 0 ----- [ anon ]
|
||||
00007fb12b726000 504 28 28 rw--- [ anon ]
|
||||
00007fb12b7a4000 12 0 0 ----- [ anon ]
|
||||
00007fb12b7a7000 504 28 28 rw--- [ anon ]
|
||||
00007fb12b825000 12 0 0 ----- [ anon ]
|
||||
...
|
||||
00007fb2731bd000 4 4 4 rw--- libpthread-2.31.so
|
||||
00007fb2731be000 24 12 12 rw--- [ anon ]
|
||||
00007fb2731c4000 4 4 0 r---- LC_MEASUREMENT
|
||||
00007fb2731c5000 4 4 0 r---- LC_IDENTIFICATION
|
||||
00007fb2731c6000 4 4 4 rw--- [ anon ]
|
||||
00007fb2731c7000 4 4 0 r---- ld-2.31.so
|
||||
00007fb2731c8000 128 128 0 r-x-- ld-2.31.so
|
||||
00007fb2731e8000 32 32 0 r---- ld-2.31.so
|
||||
00007fb2731f0000 4 0 0 r---- [ anon ]
|
||||
00007fb2731f1000 4 4 4 r---- ld-2.31.so
|
||||
00007fb2731f2000 4 4 4 rw--- ld-2.31.so
|
||||
00007fb2731f3000 4 4 4 rw--- [ anon ]
|
||||
00007fff0ca3d000 144 48 48 rw--- [ stack ]
|
||||
00007fff0ca66000 12 0 0 r---- [ anon ]
|
||||
00007fff0ca69000 8 4 0 r-x-- [ anon ]
|
||||
ffffffffff600000 4 0 0 r-x-- [ anon ]
|
||||
---------------- ------- ------- -------
|
||||
total kB 11135288 5517748 5499792%
|
||||
```
|
||||
|
||||
#### 问题结论
|
||||
- 场景一
|
||||
- Java进程除了 Jvm内存(包括堆内存 Heap、堆外内存 Non-Heap),还有非Jvm内存,包括本地运行库、JNI本地代码
|
||||
- 参考文档 [JVM监控内存详情说明](https://help.aliyun.com/zh/arms/application-monitoring/developer-reference/jvm-monitoring-memory-details)
|
||||
|
||||
- 场景二
|
||||
- Java的jvm堆栈监控看到fullgc后内存降下来,但是Java进程实际上并没有释放,这个是因为jvm申请的堆内存会回收,但是并不会归还给操作系统,从操作系统角度看内存是不会下降的
|
||||
- jvm在回收了堆中的内存之后,并不会立刻将这部分内存归还给操作系统
|
||||
- 相反,它会保留这些内存以备将来再次使用,这样做可以减少因为频繁地向操作系统申请和释放内存而带来的性能开销
|
||||
|
||||
- 业务侧动作
|
||||
- 将JVM堆的 limit占比从75%改为60%
|
||||
- 从代码层面去减少对象的创建和长时间内存占用,减少长时间执行的代码和长时间的引用
|
||||
|
||||
- 其他排查文件
|
||||
- [持续剖析功能](https://help.aliyun.com/zh/arms/application-monitoring/user-guide/enable-continuous-profiling)
|
||||
- [慢调用链诊断利器-ARMS 代码热点](https://mp.weixin.qq.com/s/_fzVzCX4bts7RByanU_Feg)
|
||||
|
||||
## 三. 参考文档
|
||||
## 参考文档
|
||||
|
||||
- [关于Linux性能调优之内存负载调优](https://blog.51cto.com/liruilong/5930543)
|
||||
- [Linux中进程内存RSS与cgroup内存的RSS统计 - 差异](https://developer.aliyun.com/article/54407)
|
||||
Reference in New Issue
Block a user