精华内容
下载资源
问答
  • “Kubernetes”(v1.10.2)说我的pod(包含一个容器)使用大约5GB的内存 . 在容器内部,RSS更像是681MiB . 可以解释如何使用以下数据从681MiB到5GB(或描述如何从容器或在kubernetes中运行此容器的docker主机中省略的另一...

    “Kubernetes”(v1.10.2)说我的pod(包含一个容器)使用大约5GB的内存 . 在容器内部,RSS更像是681MiB . 可以解释如何使用以下数据从681MiB到5GB(或描述如何从容器或在kubernetes中运行此容器的docker主机中省略的另一个命令来弥补差异)?

    kubectl top pods说5GB:

    % kubectl top pods -l app=myapp

    NAME CPU(cores) MEMORY(bytes)

    myapp-56b947bf6d-2lcr7 39m 5039Mi

    Cadvisor报告一个相似的数字(可能来自略有不同的时间,所以请忽略小的差异):

    container_memory_usage_bytes{pod_name=~".*myapp.*"} 5309456384

    5309456384 / 1024.0 / 1024 ~= 5063 ~= 5039

    在容器内部,此文件似乎是cadvisor获取其数据的位置:

    % kubectl exec -it myapp-56b947bf6d-2lcr7 bash

    meme@myapp-56b947bf6d-2lcr7:/app# cat /sys/fs/cgroup/memory/memory.usage_in_bytes

    5309456384

    容器内的驻留集大小(RSS)不匹配(小于1GB):

    meme@myapp-56b947bf6d-2lcr7:/app# kb=$(ps aux | grep -v grep | grep -v 'ps aux' | grep -v bash | grep -v awk | grep -v RSS | awk '{print $6}' | awk '{s+=$1} END {printf "%.0f", s}'); mb=$(expr $kb / 1024); printf "Kb: $kb\nMb: $mb\n"

    Kb: 698076

    Mb: 681

    完整的ps aux以防有用:

    meme@myapp-56b947bf6d-2lcr7:/app# ps aux | grep -v grep | grep -v 'ps aux' | grep -v bash | grep -v awk

    USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND

    meme 1 0.0 0.0 151840 10984 ? Ss Jun04 0:29 /usr/sbin/apache2 -D FOREGROUND

    www-data 10 0.0 0.0 147340 4652 ? S Jun04 0:00 /usr/sbin/apache2 -D FOREGROUND

    www-data 11 0.0 0.0 148556 4392 ? S Jun04 0:16 /usr/sbin/apache2 -D FOREGROUND

    www-data 12 0.2 0.0 2080632 11348 ? Sl Jun04 31:58 /usr/sbin/apache2 -D FOREGROUND

    www-data 13 0.1 0.0 2080384 10980 ? Sl Jun04 18:12 /usr/sbin/apache2 -D FOREGROUND

    www-data 68 0.3 0.0 349048 94272 ? Sl Jun04 47:09 hotapp

    www-data 176 0.2 0.0 349624 92888 ? Sl Jun04 43:11 hotapp

    www-data 179 0.2 0.0 349196 94456 ? Sl Jun04 42:20 hotapp

    www-data 180 0.3 0.0 349828 95112 ? Sl Jun04 44:14 hotapp

    www-data 185 0.3 0.0 346644 91948 ? Sl Jun04 43:49 hotapp

    www-data 186 0.3 0.0 346208 91568 ? Sl Jun04 44:27 hotapp

    www-data 189 0.2 0.0 350208 95476 ? Sl Jun04 41:47 hotapp

    来自docker的容器统计API的内存部分:

    curl --unix-socket /var/run/docker.sock 'http:/v1.24/containers/a45fc651e7b12f527b677e6a46e2902786bee6620484922016a135e317a42b4e/stats?stream=false' | jq . # yields:

    "memory_stats": {

    "usage": 5327712256,

    "max_usage": 5368344576,

    "stats": {

    "active_anon": 609095680,

    "active_file": 74457088,

    "cache": 109944832,

    "dirty": 28672,

    "hierarchical_memory_limit": 5368709120,

    "inactive_anon": 1687552,

    "inactive_file": 29974528,

    "mapped_file": 1675264,

    "pgfault": 295316278,

    "pgmajfault": 77,

    "pgpgin": 85138921,

    "pgpgout": 84964308,

    "rss": 605270016,

    "rss_huge": 0,

    "shmem": 5513216,

    "total_active_anon": 609095680,

    "total_active_file": 74457088,

    "total_cache": 109944832,

    "total_dirty": 28672,

    "total_inactive_anon": 1687552,

    "total_inactive_file": 29974528,

    "total_mapped_file": 1675264,

    "total_pgfault": 295316278,

    "total_pgmajfault": 77,

    "total_pgpgin": 85138921,

    "total_pgpgout": 84964308,

    "total_rss": 605270016,

    "total_rss_huge": 0,

    "total_shmem": 5513216,

    "total_unevictable": 0,

    "total_writeback": 0,

    "unevictable": 0,

    "writeback": 0

    },

    "limit": 5368709120

    },

    总计(memory.usage_in_bytes)= rss缓存

    usage_in_bytes:为了提高效率,作为其他内核组件,内存cgroup使用一些优化来避免不必要的cacheline错误共享 . usage_in_bytes受该方法的影响,并且不显示内存(和交换)使用的“确切”值,它是高效访问的模糊值 . (当然,必要时,它是同步的 . )如果你想知道更精确的内存使用,你应该在memory.stat中使用RSS CACHE(SWAP)值(见5.2) .

    注意:在Linux上,Docker CLI通过从总内存使用量中减去页面缓存使用情况来报告内存使用情况 . API不执行此类计算,而是提供总内存使用量和页面缓存中的金额,以便客户端可以根据需要使用数据 .

    事实上,容器中/sys/fs/cgroup/memory/memory.stat中的大部分内容都显示在上面的docker stats api响应中(略有差异来自于在不同时间采样,抱歉):

    meme@myapp-56b947bf6d-2lcr7:/app# cat /sys/fs/cgroup/memory/memory.stat

    cache 119492608

    rss 607436800

    rss_huge 0

    shmem 5525504

    mapped_file 1675264

    dirty 69632

    writeback 0

    pgpgin 85573974

    pgpgout 85396501

    pgfault 296366011

    pgmajfault 80

    inactive_anon 1687552

    active_anon 611213312

    inactive_file 32800768

    active_file 81166336

    unevictable 0

    hierarchical_memory_limit 5368709120

    total_cache 119492608

    total_rss 607436800

    total_rss_huge 0

    total_shmem 5525504

    total_mapped_file 1675264

    total_dirty 69632

    total_writeback 0

    total_pgpgin 85573974

    total_pgpgout 85396501

    total_pgfault 296366011

    total_pgmajfault 80

    total_inactive_anon 1687552

    total_active_anon 611213312

    total_inactive_file 32800768

    total_active_file 81166336

    total_unevictable 0

    记忆信息来自 kubectl describe pod :

    Limits:

    memory: 5Gi

    Requests:

    memory: 4Gi

    这是容器内 pmap 所说的内容 . 在这个单行程序中,我获取所有进程ID,在它们上运行pmap -x,并从pmap结果中提取Kbytes列 . 总结果是256兆字节(远小于ps的RSS,部分,我认为,因为许多进程没有返回pmap -x的输出):

    ps aux | awk '{print $2}' | grep -v PID | xargs sudo pmap -x | grep total | grep -v grep | awk '{print $3}' | awk '{s+=$1} END {printf "%.0f", s}'; echo

    256820

    ps_mem.py在https://stackoverflow.com/a/133444/6090676提及 . 它检查 /proc/$pid/statm 和 /proc/$pid/smaps . 这里没有照明(再次,似乎忽略了一些过程):

    # python ps_mem.py

    Private + Shared = RAM used Program

    1.7 MiB + 1.0 MiB = 2.7 MiB apache2

    2.0 MiB + 1.0 MiB = 3.0 MiB bash (3)

    ---------------------------------

    5.7 MiB

    =================================

    展开全文
  • 指标解释:容器的内存使用率是读取物理机cgroup下面的文件的,获取的是整个容器的内存使用率并不是针对某个程序。物理机内存使用率和使用free命令计算结果是一致的。物理机和容器两者内存计算数据是独立的 解决步骤...

    零、总结:

    本次问题通过分析,由于平时70%+的内存使用率,目前达到88%是由于5个月系统未重新发布内存数据和缓存不断增加以及堆内存的增加累计达到了内存使用率的报警阀值88%。
    在这里插入图片描述

    那么平时如果出现内存使用率偏高的问题,应该如何解决呢?下面的几个步骤其实就是从硬件-》系统-》进程来由大到小解决的。
    1、由于内存分配问题(也就是我这里的问题,对应解决办法如步骤1和2),


    2019-1-21 19:57:00 更新
    在深入理解java虚拟机(第二版)周志明的第5章中有高性能硬件上的程序部署策略,有一些指引方向,当时我排查问题还没看该书。特此在这里补充下。


    2、长期持有super big对象耗内存(对应解决办法如步骤3)
    3、死锁问题(对应解决办法如步骤4)
    4、其他原因比如poll长连接或者其他导致并发线程增多的原因(对应解决办法如步骤5和6)
    5、定位某个进程的内存什么问题(如步骤7)
    6、线程具体什么代码或者什么原因导致的(如步骤8)
    对于jvm8+调优点击下面
    参考实战和指导手册

    如果你是小白码农,还没有到达码工的层级,那么可以按照如下的教程定位问题。如果依然有疑问可以关注公众号【小诚信驿站】或者加 QQ群300458205

    一、知识点了解:

    了解下硬件和系统和进程之间的关系。
    在这里插入图片描述
    1.1硬件:
    top执行命令可以得到

    Cpu(s):  0.0%us,  0.3%sy,  0.0%ni, 99.7%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
    Mem:     73728k total,    70048k used,     3680k free,        0k buffers
    Swap:    16384k total,     4696k used,    11688k free,    64716k cached
    
    top看到的内存使用情况,有一部分是缓存,mem那行后面有个buffers ,swap那行后面有个cached,这两个就是缓存大小。所以如果要计算应用程序真正使用物理内存的情况,应该是used-cached-buffers才对,所以刚才top看到的物理内存使用情况为70048k-64716k=5332k。
    

    如果想要直接看内存使用情况可以执行 free命令 后面加个m是以M为单位显示

    free -m
    -----------
                  total       used       free     shared    buffers     cached
    Mem:            72         69          2          0          0         63
    -/+ buffers/cache:          5         66
    Swap:           16          4         11
    
    其中第一行用全局角度描述系统使用的内存状况: 
    total——总物理内存 
    used——已使用内存,一般情况这个值会比较大,因为这个值包括了cache+应用程序使用的内存 
    free——完全未被使用的内存 
    shared——应用程序共享内存 多个进程之间共享的内存部分,比如公共库libc.so等
    buffers——缓存,主要用于目录方面,inode值等(ls大目录可看到这个值增加)缓存将要放到硬盘里的数据 
    cached——缓存,缓存从硬盘读出来的数据用于已打开的文件:
    当你读写文件的时候,Linux内核为了提高读写性能与速度,会将文件在内存中进行缓存,这部分内存就是Cache Memory(缓存内存)。即使你的程序运行结束后,Cache Memory也不会自动释放。这就会导致你在Linux系统中程序频繁读写文件后,你会发现可用物理内存会很少。 
    其实这缓存内存(Cache Memory)在你需要使用内存的时候会自动释放,所以你不必担心没有内存可用。 
    只有当 free 减去 cached 剩下的这部分内存情况紧张时,才有可能出现应用程序没有足够内存使用的情况 
    注意-/+ buffers/cache: 5 66这行。 
    前个值表示-buffers/cache—–>不包括缓存,应用程序物理内存使用情况,即 -buffers/cache=used-buffers-cached ,所以此时应用程序才用了5M内存 。 
    后个值表示+buffers/cache—–>所有可供应用程序使用的内存大小,free加上缓存值,即+buffers/cache=free+buffers+cached ,所以此时还有接近66M 内存可供程序使用。 
    
    swap:
    交换分区、交互内存:
    
    交互分区属于硬盘空间,做为内存不足时的临时内存使用
    swap 主要的功能是当实体内存不够时,则某些在内存当中所占的程序会暂时被移动到 swap 当中,让实体内存可以被需要的程序来使用。另外,如果你的主机支持电源管理模式, 也就是说,你的 Linux 主机系统可以进入“休眠”模式的话,那么, 运行当中的程序状态则会被纪录到 swap 去,以作为“唤醒”主机的状态依据! 另外,有某些程序在运行时,本来就会利用 swap 的特性来存放一些数据段, 所以, swap 来是需要创建的!只是不需要太大!
    
    

    1.2系统:

    虚拟内存是操作系统内核为了对进程地址空间进行管理(process address space management)而精心设计的一个逻辑意义上的内存空间概念。我们程序中的指针其实都是这个虚拟内存空间中的地址。比如我们在写完一段C++程序之后都需要采用g++进行编译,这时候编译器采用的地址其实就是虚拟内存空间的地址。因为这时候程序还没有运行,何谈物理内存空间地址?凡是程序运行过程中可能需要用到的指令或者数据都必须在虚拟内存空间中。既然说虚拟内存是一个逻辑意义上(假象的)的内存空间,为了能够让程序在物理机器上运行,那么必须有一套机制可以让这些假象的虚拟内存空间映射到物理内存空间(实实在在的RAM内存条上的空间)。这其实就是操作系统中页映射表(page table)所做的事情了。内核会为系统中每一个进程维护一份相互独立的页映射表。页映射表的基本原理是将程序运行过程中需要访问的一段虚拟内存空间通过页映射表映射到一段物理内存空间上,这样CPU访问对应虚拟内存地址的时候就可以通过这种查找页映射表的机制访问物理内存上的某个对应的地址。“页(page)”是虚拟内存空间向物理内存空间映射的基本单元。
    

    1.3进程
    拿java举例
    在这里插入图片描述

    VSS- Virtual Set Size 虚拟耗用内存(包含共享库占用的内存)
    RSS- Resident Set Size 实际使用物理内存(包含共享库占用的内存)
    PSS- Proportional Set Size 实际使用的物理内存(比例分配共享库占用的内存)
    USS- Unique Set Size 进程独自占用的物理内存(不包含共享库占用的内存)
    一般来说内存占用大小有如下规律:VSS >= RSS >= PSS >= USS
    

    二、问题:

    内存使用率88%高于80%报警。

    三、原因:

    指标含义:内存使用率百分比(%)。
    指标解释:容器的内存使用率是读取物理机cgroup下面的文件的,获取的是整个容器的内存使用率并不是针对某个程序。物理机内存使用率和使用free命令计算结果是一致的。物理机和容器两者内存计算数据是独立的
    计算公式近似等于为:进程使用的(物理内存和本地内存和共享内存)、未被换出的物理内存大小,单位kb。RES=CODE+DATA

    四、解决步骤:

    用top中查看RES是操作系统角度看jvm的内存占用。
    用jmap查看的堆内存,是用jvm的角度看jvm内部程序的内存占用。
    存在差异是因为jvm有一些共享库和共享内存,被操作系统计入RES中,但未被jvm计入

    1、查看哪些应用占用内存比较大

    查看哪几个进程内存占用最高:top -c,输入大写M,以内存使用率从高到低排序
    

    在这里插入图片描述

    PID : 进程id
    PPID : 父进程id
    RUSER : Real user name
    UID : 进程所有者的用户id
    USER : 进程所有者的用户名
    GROUP : 进程所有者的组名
    TTY : 启动进程的终端名。不是从终端启动的进程则显示为 ?
    PR : 优先级
    NI : nice值。负值表示高优先级,正值表示低优先级
    P : 最后使用的CPU,仅在多CPU环境下有意义
    %CPU : 上次更新到现在的CPU时间占用百分比
    TIME : 进程使用的CPU时间总计,单位秒
    TIME+ : 进程使用的CPU时间总计,单位1/100秒
    %MEM : 进程使用的物理内存百分比
    VIRT : 进程使用的虚拟内存总量,单位kb。VIRT=SWAP+RES
    SWAP : 进程使用的虚拟内存中,被换出的大小,单位kb。
    RES : 进程使用的、未被换出的物理内存大小,单位kb。RES=CODE+DATA
    CODE : 可执行代码占用的物理内存大小,单位kb
    DATA : 可执行代码以外的部分(数据段+栈)占用的物理内存大小,单位kb
    SHR : 共享内存大小,单位kb
    nFLT : 页面错误次数
    nDRT : 最后一次写入到现在,被修改过的页面数。
    S : 进程状态。D=不可中断的睡眠状态 R=运行 S=睡眠 T=跟踪/停止 Z=僵尸进程
    COMMAND : 命令名/命令行
    WCHAN : 若该进程在睡眠,则显示睡眠中的系统函数名
    Flags : 任务标志,参考 sched.h
    
    默认情况下仅显示比较重要的 PID、USER、PR、NI、VIRT、RES、SHR、S、%CPU、%MEM、TIME+、COMMAND 列。可以通过下面的快捷键来更改显示内容。 更改显示内容
    通过 f 键可以选择显示的内容。按 f 键之后会显示列的列表,按 a-z 即可显示或隐藏对应的列,最后按回车键确定。
    按 o 键可以改变列的显示顺序。按小写的 a-z 可以将相应的列向右移动,而大写的 A-Z 可以将相应的列向左移动。最后按回车键确定。
    按大写的 F 或 O 键,然后按 a-z 可以将进程按照相应的列进行排序。而大写的 R 键可以将当前的排序倒转
    

    2、通过jmap -heap 进程id 命令排除是由于堆分配内存问题。得到如下结果

    Attaching to process ID 542287, please wait...
    Debugger attached successfully.
    Server compiler detected.
    JVM version is 25.20-b23
    
    using thread-local object allocation.
    Garbage-First (G1) GC with 43 thread(s)
    //堆配置信息
    Heap Configuration:
       //指定 jvm heap 在使用率小于 n 的情况下 ,heap 进行收缩 ,Xmx==Xms 的情况下无效 , 如 
       MinHeapFreeRatio         = 40
       //指定 jvm heap 在使用率大于 n 的情况下 ,heap 进行扩张 ,Xmx==Xms 的情况下无效 , 如 
       MaxHeapFreeRatio         = 70
       //最大堆空间
       MaxHeapSize              = 5393874944 (5144.0MB)
       //设置Yong Generation的初始值大小,一般情况下,不允许-XX:Newratio值小于1,即Old要比Yong大。
       NewSize                  = 1363144 (1.2999954223632812MB)
       //设置Yong Generation的最大值大小
       MaxNewSize               = 3235905536 (3086.0MB)
       OldSize                  = 5452592 (5.1999969482421875MB)
       //设置年轻代和老年代的比例,默认情况下,此选项为2
       NewRatio                 = 2
       //默认eden空间大小和survivor空间大小的比,默认情况下为8
       SurvivorRatio            = 8
       //初始化元空间大小,控制gc阀值,gc后动态增加或者降低元空间大小,默认情况下平台的不同,步长为12-20M
       MetaspaceSize            = 209715200 (200.0MB)
       //默认1G,这个参数主要是设置Klass Metaspace的大小,不过这个参数设置了也不一定起作用,前提是能开启压缩指针,假如-Xmx超过了32G,压缩指针是开启不来的。如果有Klass Metaspace,那这块内存是和Heap连着的。
       CompressedClassSpaceSize = 1073741824 (1024.0MB)
       //为类元数据分配的最大空间量
       MaxMetaspaceSize         = 536870912 (512.0MB)
       //堆内存中一个Region的大小可以通过-XX:G1HeapRegionSize参数指定,大小区间只能是1M、2M、4M、8M、16M和32M,总之是2的幂次方,如果G1HeapRegionSize为默认值,则在堆初始化时计算Region的实践大小
       G1HeapRegionSize         = 2097152 (2.0MB)
    //堆的使用信息
    Heap Usage:
    G1 Heap:
    //区域数量
       regions  = 2572
    //堆内存大小
       capacity = 5393874944 (5144.0MB)
    //已经使用了
       used     = 3216639400 (3067.62638092041MB)
    //空闲着的堆内存
       free     = 2177235544 (2076.37361907959MB)
       59.63503850933923% used
    以下同理
    G1 Young Generation:
    Eden Space:
       regions  = 425
       capacity = 2650800128 (2528.0MB)
       used     = 891289600 (850.0MB)
       free     = 1759510528 (1678.0MB)
       33.62341772151899% used
    Survivor Space:
       regions  = 1
       capacity = 2097152 (2.0MB)
       used     = 2097152 (2.0MB)
       free     = 0 (0.0MB)
       100.0% used
    G1 Old Generation:
       regions  = 1109
       capacity = 2740977664 (2614.0MB)
       used     = 2323252648 (2215.62638092041MB)
       free     = 417725016 (398.37361907958984MB)
       84.75999927009985% used
    
    35394 interned Strings occupying 3871104 bytes.
    

    截止到这里,本次问题已经找到了。因为设置的堆空间分配额比较大。将近63% =5g/8g。内存使用率计算公式为code+Data。本地内存和共享内存和可执行代码以外的部分(数据段+栈)等,当堆内存还没有达到full gc的时候,内存使用率问题就显现出来了。将内存分配最大值设为4g.并重新更新配置文件,发布应用
    在这里插入图片描述
    在这里插入图片描述
    但是这里存在一个问题,内存使用率高,刚才提到的一个情况就是堆内存接近最大值不会进行fullgc么?fullgc不就帮你回收堆空间了么?
    这是个好的问题。
    实际上他确实发生fullgc了,我们可以查到
    在这里插入图片描述
    在这里插入图片描述
    那么为什么没有解决内存使用率问题呢?而是将堆分配额重新调整之后,内存使用率才降下去。
    简单点说,就是如果你的项目需要人手10个人,你跟领导要了10个人,当项目只是第一个迭代干完了,那么你会不会立马将其中的5个人交给领导?答案是不会的,但是如果现在重新分配,领导说我就给你5个人下一个迭代你先干着,这样你就需要必须上交5个人。具体的点这里。详细的说明内存是如何管理的。


    如果上面也没有问题定位到原因,则继续按照步骤排查

    3、找到最耗内存的对象
    jmap -histo 进程ID(带上:live则表示先进行一次FGC再统计,如jmap -histo:live 进程ID)

    在这里插入图片描述
    可以看到上面最大的实例进程 将近30M。

    4、导出内存转储快照dump文件:
    4.1、通过java进程命令定位 系统进程并使用jmap工具dump文件。

    ps -ef | grep java 
    生成dump文件的命令:
    jmap -dump:format=b,file=20181218.dump 16048
    file后面的是自定义的文件名,最后的数字是进程的pid。
    

    4.2、使用jvisualvm来分析dump文件:

    jvisualvm是JDK自带的Java性能分析工具,在JDK的bin目录下,文件名就叫jvisualvm.exe。
    jvisualvm可以监控本地、远程的java进程,实时查看进程的cpu、堆、线程等参数,对java进程生成dump文件,并对dump文件进行分析。
    假设我现在下载下来的是txt文件也可以直接扔给jvisualvm来分析。
    

    在这里插入图片描述

    4.3、使用方式:直接双击打开jvisualvm.exe,点击文件->装入,在文件类型那一栏选择堆,选择要分析的dump文件,打开。

    导入文件以后界面如下图:
    在这里插入图片描述
    在这里插入图片描述

    可以看到,dump文件里记录的堆中的实例,总大小大概5392M左右,(用第一行的实例大小除以百分比就能算出来)

    4.4、现在看堆转储的线程问题

    在这里插入图片描述
    每一个部分的含义如下:
    “http-nio-1601-Acceptor-0” 线程名称
    daemon 线程的类型
    prio=5 线程的优先级别
    tid=290 线程ID
    RUNNABLE 线程当前的状态

    4.5、线程当前的状态是我们主要关注的内容。
    dump文件中描述的线程状态

    runnable:运行中状态,在虚拟机内部执行,可能已经获取到了锁,可以观察是否有locked字样。
    blocked:被阻塞并等待锁的释放。
    wating:处于等待状态,等待特定的操作被唤醒,一般停留在park(), wait(), sleep(),join() 等语句里。
    time_wating:有时限的等待另一个线程的特定操作。
    terminated:线程已经退出

    4.6、进程的区域划分

    进入区(Entry Set):等待获取对象锁,一旦对象锁释放,立即参与竞争。
    拥有区(The Owner):已经获取到锁。
    等待区(Wait Set):表示线程通过wait方法释放了对象锁,并在等待区等待被唤醒。

    4.7、方法调用修饰

    locked: 成功获取锁
    waiting to lock:还未获取到锁,在进入去等待;
    waiting on:获取到锁之后,又释放锁,在等待区等待;

    4.8、OQL(对象查询语言)
    如果需要根据某些条件来过滤或查询堆的对象,比如现在我们查询下系统中类加载器一共有几种?

    在这里插入图片描述

    4.9、引导计数
    引导类 (即 JVM 在未使用任何 java.lang.ClassLoader 实例的情况下加载的 Java 平台类) 的计数
    其余展示的与名称一样

    5、统计进程打开的句柄数:ls /proc/进程ID/fd |wc -l

    6、统计进程打开的线程数:ls /proc/进程ID/task |wc -l

    7、使用jstat查看进程的内存使用情况
    jstat [Options] vmid [interval] [count]
    Options,选项,我们一般使用 -gcutil 查看gc情况
    vmid,VM的进程号,即当前运行的java进程号
    interval,间隔时间,单位为秒或者毫秒
    count,打印次数,如果缺省则打印无数次

      S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT   
      0.00 100.00  32.20   7.05  48.98  95.35 102490 10125.674     1   39.100 10164.775
      0.00 100.00  32.57   7.05  48.98  95.35 102490 10125.674     1   39.100 10164.775
      0.00 100.00  32.94   7.05  48.98  95.35 102490 10125.674     1   39.100 10164.775
      0.00 100.00  33.31   7.05  48.98  95.35 102490 10125.674     1   39.100 10164.775
      0.00 100.00  33.62   7.05  48.98  95.35 102490 10125.674     1   39.100 10164.775
    
     S0C:年轻代中第一个survivor(幸存区)的容量 (字节) 
    S1C:年轻代中第二个survivor(幸存区)的容量 (字节) 
    S0U:年轻代中第一个survivor(幸存区)目前已使用空间 (字节) 
    S1U:年轻代中第二个survivor(幸存区)目前已使用空间 (字节) 
    EC:年轻代中Eden(伊甸园)的容量 (字节) 
    EU:年轻代中Eden(伊甸园)目前已使用空间 (字节) 
    OC:Old代的容量 (字节) 
    OU:Old代目前已使用空间 (字节) 
    PC:Perm(持久代)的容量 (字节) 
    PU:Perm(持久代)目前已使用空间 (字节) 
    YGC:从应用程序启动到采样时年轻代中gc次数 
    YGCT:从应用程序启动到采样时年轻代中gc所用时间(s) 
    FGC:从应用程序启动到采样时old代(全gc)gc次数 
    FGCT:从应用程序启动到采样时old代(全gc)gc所用时间(s) 
    GCT:从应用程序启动到采样时gc用的总时间(s) 
    NGCMN:年轻代(young)中初始化(最小)的大小 (字节) 
    NGCMX:年轻代(young)的最大容量 (字节) 
    NGC:年轻代(young)中当前的容量 (字节) 
    OGCMN:old代中初始化(最小)的大小 (字节) 
    OGCMX:old代的最大容量 (字节) 
    OGC:old代当前新生成的容量 (字节) 
    PGCMN:perm代中初始化(最小)的大小 (字节) 
    PGCMX:perm代的最大容量 (字节) 
    PGC:perm代当前新生成的容量 (字节) 
    S0:年轻代中第一个survivor(幸存区)已使用的占当前容量百分比 
    S1:年轻代中第二个survivor(幸存区)已使用的占当前容量百分比 
    E:年轻代中Eden(伊甸园)已使用的占当前容量百分比 
    O:old代已使用的占当前容量百分比 
    P:perm代已使用的占当前容量百分比 
    M:元空间中已使用的占当前容量百分比
    S0CMX:年轻代中第一个survivor(幸存区)的最大容量 (字节) 
    S1CMX :年轻代中第二个survivor(幸存区)的最大容量 (字节) 
    ECMX:年轻代中Eden(伊甸园)的最大容量 (字节) 
    DSS:当前需要survivor(幸存区)的容量 (字节)(Eden区已满) 
    TT: 持有次数限制 
    MTT : 最大持有次数限制
    
    

    8、.用jstack查看一下
    jstack pid | grep tid(线程ID) -A 30

    在这里插入图片描述

    展开全文
  • 容器内存分析

    千次阅读 2020-04-03 13:58:46
    那么如何准确计算容器或Pod的内存使用率,k8s/docker又是如何计算,本文通过实验与源码阅读相结合来分析容器的内存实际使用量。 预备知识 不管docker还是k8s(通过cadvisor)最终都通过cgroup的memory group来得到内存...

    背景

    在容器化环境中,平台需要提供准确的业务监控指标,已方便业务查看。那么如何准确计算容器或Pod的内存使用率,k8s/docker又是如何计算,本文通过实验与源码阅读相结合来分析容器的内存实际使用量。

    预备知识

    不管docker还是k8s(通过cadvisor)最终都通过cgroup的memory group来得到内存的原始文件,memory相关的主要文件如下:

    cgroup.event_control       #用于eventfd的接口
    memory.usage_in_bytes      #显示当前已用的内存
    memory.limit_in_bytes      #设置/显示当前限制的内存额度
    memory.failcnt             #显示内存使用量达到限制值的次数
    memory.max_usage_in_bytes  #历史内存最大使用量
    memory.soft_limit_in_bytes #设置/显示当前限制的内存软额度
    memory.stat                #显示当前cgroup的内存使用情况
    memory.use_hierarchy       #设置/显示是否将子cgroup的内存使用情况统计到当前cgroup里面
    memory.force_empty         #触发系统立即尽可能的回收当前cgroup中可以回收的内存
    memory.pressure_level      #设置内存压力的通知事件,配合cgroup.event_control一起使用
    memory.swappiness          #设置和显示当前的swappiness
    memory.move_charge_at_immigrate #设置当进程移动到其他cgroup中时,它所占用的内存是否也随着移动过去
    memory.oom_control         #设置/显示oom controls相关的配置
    memory.numa_stat           #显示numa相关的内存
    

    更多信息可参考Pod memory usage in k8s

    查看源码

    docker stat

    docker stat的源码在stats_helpers.go,如下:

    func calculateMemUsageUnixNoCache(mem types.MemoryStats) float64 {
        return float64(mem.Usage - mem.Stats["cache"])
    }
    

    内存使用量为memory.usage=memory.usage_in_bytes-cache

    kubectl top

    在k8s中,kubectl top命令通过metric-server/heapster获取cadvisor中working_set的值,来表示Pod实例使用内存大小(不包括pause),metrics-server 中pod内存获取如下:

    func decodeMemory(target *resource.Quantity, memStats *stats.MemoryStats) error {
    	if memStats == nil || memStats.WorkingSetBytes == nil {
    		return fmt.Errorf("missing memory usage metric")
    	}
    
    	*target = *uint64Quantity(*memStats.WorkingSetBytes, 0)
    	target.Format = resource.BinarySI
    
    	return nil
    }
    

    cadvisor中working_set计算如下:

    func setMemoryStats(s *cgroups.Stats, ret *info.ContainerStats) {
    	ret.Memory.Usage = s.MemoryStats.Usage.Usage
    	ret.Memory.MaxUsage = s.MemoryStats.Usage.MaxUsage
    	ret.Memory.Failcnt = s.MemoryStats.Usage.Failcnt
    
    	if s.MemoryStats.UseHierarchy {
    		ret.Memory.Cache = s.MemoryStats.Stats["total_cache"]
    		ret.Memory.RSS = s.MemoryStats.Stats["total_rss"]
    		ret.Memory.Swap = s.MemoryStats.Stats["total_swap"]
    		ret.Memory.MappedFile = s.MemoryStats.Stats["total_mapped_file"]
    	} else {
    		ret.Memory.Cache = s.MemoryStats.Stats["cache"]
    		ret.Memory.RSS = s.MemoryStats.Stats["rss"]
    		ret.Memory.Swap = s.MemoryStats.Stats["swap"]
    		ret.Memory.MappedFile = s.MemoryStats.Stats["mapped_file"]
    	}
    	if v, ok := s.MemoryStats.Stats["pgfault"]; ok {
    		ret.Memory.ContainerData.Pgfault = v
    		ret.Memory.HierarchicalData.Pgfault = v
    	}
    	if v, ok := s.MemoryStats.Stats["pgmajfault"]; ok {
    		ret.Memory.ContainerData.Pgmajfault = v
    		ret.Memory.HierarchicalData.Pgmajfault = v
    	}
    
    	workingSet := ret.Memory.Usage
    	if v, ok := s.MemoryStats.Stats["total_inactive_file"]; ok {
    		if workingSet < v {
    			workingSet = 0
    		} else {
    			workingSet -= v
    		}
    	}
    	ret.Memory.WorkingSet = workingSet
    }
    

    working_set=memory.usage_in_bytes-total_inactive_file (>=0)
    在kubelet中节点内存不足时同样以working_set判断pod是否OOM的标准

    实验

    1. 创建Pod
      Pod的资源申请如下:
            resources:
              limits:
                cpu: "1"
                memory: 1Gi
              requests:
                cpu: "0"
                memory: "0"
    
    1. 查看cgroup内存情况
      找到容器某个进程,查看memory cgroup
    # cat /proc/16062/cgroup 
    ...
    8:memory:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod21a55da5_f9f8_11e9_b051_fa163e7e981a.slice/docker-57ba1991ab4ba50a9b2eaf5bf90e2c20073198d767653becf77d55ee25e1a6f9.scope
    

    进入容器memory cgroup对应的目录

    docker-57ba1991ab4ba50a9b2eaf5bf90e2c20073198d767653becf77d55ee25e1a6f9.scope]# ls
    cgroup.clone_children  memory.kmem.failcnt             memory.kmem.tcp.limit_in_bytes      memory.max_usage_in_bytes        memory.move_charge_at_immigrate  memory.stat            tasks
    cgroup.event_control   memory.kmem.limit_in_bytes      memory.kmem.tcp.max_usage_in_bytes  memory.memsw.failcnt             memory.numa_stat                 memory.swappiness
    cgroup.procs           memory.kmem.max_usage_in_bytes  memory.kmem.tcp.usage_in_bytes      memory.memsw.limit_in_bytes      memory.oom_control               memory.usage_in_bytes
    memory.failcnt         memory.kmem.slabinfo            memory.kmem.usage_in_bytes          memory.memsw.max_usage_in_bytes  memory.pressure_level            memory.use_hierarchy
    memory.force_empty     memory.kmem.tcp.failcnt         memory.limit_in_bytes               memory.memsw.usage_in_bytes      memory.soft_limit_in_bytes       notify_on_release
    

    查看主要memory文件

    # cat memory.limit_in_bytes (容器memory limit值,即1Gi)
    1073741824
    [root@node01 docker-57ba1991ab4ba50a9b2eaf5bf90e2c20073198d767653becf77d55ee25e1a6f9.scope]# cat memory.kmem.limit_in_bytes (容器内核使用memory limit值)
    9223372036854771712
    [root@node01 docker-57ba1991ab4ba50a9b2eaf5bf90e2c20073198d767653becf77d55ee25e1a6f9.scope]# 
    [root@node01 docker-57ba1991ab4ba50a9b2eaf5bf90e2c20073198d767653becf77d55ee25e1a6f9.scope]# cat memory.soft_limit_in_bytes
    9223372036854771712
    [docker-57ba1991ab4ba50a9b2eaf5bf90e2c20073198d767653becf77d55ee25e1a6f9.scope]# cat notify_on_release
    0
    [docker-57ba1991ab4ba50a9b2eaf5bf90e2c20073198d767653becf77d55ee25e1a6f9.scope]# cat memory.oom_control 
    oom_kill_disable 0
    under_oom 0
    oom_kill 0
    [docker-57ba1991ab4ba50a9b2eaf5bf90e2c20073198d767653becf77d55ee25e1a6f9.scope]# cat memory.usage_in_bytes 
    2265088
    [docker-57ba1991ab4ba50a9b2eaf5bf90e2c20073198d767653becf77d55ee25e1a6f9.scope]# cat memory.kmem.usage_in_bytes 
    901120
    [docker-57ba1991ab4ba50a9b2eaf5bf90e2c20073198d767653becf77d55ee25e1a6f9.scope]# cat memory.stat 
    cache 12288
    rss 1351680
    rss_huge 0
    shmem 4096
    mapped_file 4096
    dirty 0
    writeback 0
    swap 0
    pgpgin 4544
    pgpgout 4211
    pgfault 1948
    pgmajfault 0
    inactive_anon 4096
    active_anon 1351680
    inactive_file 8192
    active_file 0
    unevictable 0
    hierarchical_memory_limit 1073741824
    hierarchical_memsw_limit 1073741824
    total_cache 12288
    total_rss 1351680
    total_rss_huge 0
    total_shmem 4096
    total_mapped_file 4096
    total_dirty 0
    total_writeback 0
    total_swap 0
    total_pgpgin 4544
    total_pgpgout 4211
    total_pgfault 1948
    total_pgmajfault 0
    total_inactive_anon 4096
    total_active_anon 1351680
    total_inactive_file 8192
    total_active_file 0
    total_unevictable 0
    

    根据memory可得到如下关系:
    memory.usage_in_bytes = memory.kmem.usage_in_bytes + rss + cache
    即2265088=901120+1351680+12288

    那么容器的真实内存即:
    memory.usage=memory.usage_in_bytes-cache
    rss+kmem_usage

    通过docker stat查看,与公式相符合

    CONTAINER ID        NAME                                                                                     CPU %               MEM USAGE / LIMIT   MEM %               NET I/O             BLOCK I/O           PIDS
    57ba1991ab4b        k8s...default_21a55da5-f9f8-11e9-b051-fa163e7e981a_0   0.00%               2.148MiB / 1GiB     0.21%               12MB / 68.8MB       0B / 0B             2
    

    结论

    实际环境中,docker与k8s两种内存表示方式不同,一般docker stat总体值会小于kubectl top

    • docker中内存表示为:
      memory.usage = memory.usage_in_bytes - cache
    • k8s中:
      memory.usage = working_set = memory.usage_in_bytes - total_inactive_file (>=0)
      根据cgroup memory关系有:
      memory.usage_in_bytes = memory.kmem.usage_in_bytes + rss + cache

    真实环境中两种表示相差不大,但更推荐使用working_set作为容器内存真实使用量(kubelt判断OOM的依据),
    则容器内存使用率可表示为:
    container_memory_working_set_bytes / memory.limit_in_bytes

    参考

    1. https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt
    2. https://medium.com/@zhimin.wen/memory-limit-of-pod-and-oom-killer-891ee1f1cad8
    展开全文
  • 简介: Metricbeat是一个轻量级托运工,你可以在服务器上安装它,...可以获取系统级 CPU 使用率内存、文件系统、磁盘 IO 和网络 IO 统计数据,还可针对系统上每个进程获得与 top 命令类似统计数据。 ...

    简介:

    Metricbeat是一个轻量级的托运工,你可以在服务器上安装它,定期从操作系统和服务器上运行的服务收集指标,Metricbeat取得它收集的指标和统计数据,并将它们发送到你指定的输出,例如Elasticsearch或Logstash。

    可以获取系统级的 CPU 使用率、内存、文件系统、磁盘 IO 和网络 IO 统计数据,还可针对系统上的每个进程获得与 top 命令类似的统计数据。

    安装Metricbeat之前需要有Elastic Stack ,没有的话需要安装。

    安装:

    要下载和安装Metricbeat,使用你系统的命令(deb用于Debian/Ubuntu,rpm用于Redhat/Centos/Fedora,mac用于OS X,docker用于任何docker平台,win用于Windows)。

    我是linux centos7,有两种下载方式:

    1:使用rpm:

    curl -L -O https://artifacts.elastic.co/downloads/beats/metricbeat/metricbeat-6.4.2-x86_64.rpm
    sudo rpm -vi metricbeat-6.4.2-x86_64.rpm

    2:下载压缩包:

    curl -L -O https://artifacts.elastic.co/downloads/beats/metricbeat/metricbeat-7.5.1-linux-x86_64.tar.gz
    tar xzvf metricbeat-7.5.1-linux-x86_64.tar.gz

    配置:

    启用要运行的模块。如果您接受默认配置而不启用其他模块,则Metricbeat仅收集系统指标。

    您可以启用modules.d目录中定义的默认模块配置 (推荐),也可以将模块配置添加到 metricbeat.yml文件中。该modules.d目录包含所有可用Metricbeat模块的默认配置。

    以下示例在目录中启用apachemysql

    deb and rpm:

    metricbeat modules enable apache mysql

    mac and linux:

    ./metricbeat modules enable apache mysql
    

    配置vim metricbeat.yml

    配置kibana仪表盘:

    #============================== Kibana =====================================
    
    # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API.
    # This requires a Kibana endpoint configuration.
    setup.kibana:
      host: "172.17.0.2:5601"
    setup.dashboards.enabled: true
    

    输出到ElasticSearch:

    #-------------------------- Elasticsearch output ------------------------------
    output.elasticsearch:
      # Array of hosts to connect to.
      hosts: ["172.17.0.2:9200"]
    

    配置 modules.d

    配置容器模块被监听的内容:

    [root@localhost modules.d]# vim docker.yml
    
    # Module: docker
    # Docs: https://www.elastic.co/guide/en/beats/metricbeat/7.5/metricbeat-module-docker.html
    
    - module: docker
      metricsets:
        - container
        - cpu
        - diskio
        - event
        - healthcheck
        - info
        - memory
        - network
      period: 10s
      hosts: ["unix:///var/run/docker.sock"]
    
      # If set to true, replace dots in labels with `_`.
      #labels.dedot: false
    
      # To connect to Docker over TLS you must specify a client and CA certificate.
      #ssl:
        #certificate_authority: "/etc/pki/root/ca.pem"
        #certificate:           "/etc/pki/client/cert.pem"
        #key:                   "/etc/pki/client/cert.key"
    

    启动:

    service metricbeat start
    service metricbeat restart
    service metricbeat status

    如上图显示active (running),说明正常启动,配置没有问题。

    kibana 可视化:

    可以先查看是否启动成功:

    curl -XGET 'http://172.17.0.2:9200/metricbeat-*/_search?pretty'

     OK,继续:

    网址输入:http://ip:5601查看kibana 的仪表盘:

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    展开全文
  • 默认情况下容器可以使用的主机 CPU 资源是不受限制的。和内存资源的使用一样,如果不对容器可以使用的 CPU ...本文的 demo 中会继续使用《Docker: 限制容器可用的内存》一文中创建的 docker 镜像 u-stress 进行压...
  • docker查看容器的cpu占用

    万次阅读 2019-05-24 20:05:05
    每个接口进行调用数据时都会产生很大延时,导致cpu占用不规则跳动,删除压测数据,释放了部分数据库数据,但是指标不治本,想着接下来看看mycat,redis好像压测也会因为内存使用完毕爆掉,想着接下来使用kafka...
  • 我们有一个应用,在上线之后,监控到内存可用随着运行时间逐步下降,从上线之初50%,运行一段时间后下降到20%左右。机器上有其他进程也占内存,我想确定下是否是内存泄漏导致,查清楚后也能对线上应用运行...
  • 第一次是:阿里云上采用容器化部署,接口300个并发应用cpu利用很低,接口响应时间也很长。,因为接口逻辑非常简单,故研发怀疑是网络问题导致。 经过一系列排查,是因为购买是阿里云最基础slb服务,对每...
  • devops容器云 ... AppDynamics Docker Monitoring Extension监视并报告各种指标,例如:容器总数,正在运行的容器,图像,CPU使用率,内存使用率,网络流量等。 AppDynamics Docker监控扩展使用Uni...
  • 内存使用率 修改参数 点击小齿轮 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bQvtPuGC-1591409337788)(https://gitee.com/jiaminxu/BlogImage/raw/master/img/20200602161152.png)]...
  • docker 共享宿主机JDK,优化物理内存使用 问题描述:因每个独立容器都会安装JDK环境,JDK通常内存占用1G-2G之间,所以每 一个独立容器的JDK,都会占用1G左右内存空间,共享内存可以降低物理内存的使用 。 解决...
  • NIO全称 Java Non-blocking IO 或 Java New IO,为所有原始类型(boolean类型除外)提供缓存支持数据容器,使它可以提供非阻塞式高伸缩性网络 AIO:(Async I/O):异步非阻塞IO IO 操作模式 PIO(Programing...
  • 监视您FRITZ!Box 该存储库提供了两个容器映像文档,以监视您FRITZ!... 例如,带宽使用率,正常运行时间,CPU使用率,CPU温度,内存使用率和其他类似值。 该容器需要FRITZ!Box密码才能登录以获取数据。
  • 如果我们仅在单个线程上运行所有内容,那么我们可能会发现CPU利用较低,并且感觉我们在浪费金钱来浪费未充分使用的计算机正常运行时间。 但是,当您运行带有Java应用程序之类的容器时,如何计算最终得到多...
  • Java 10改进了对Docker容器的支持

    千次阅读 2018-04-09 20:14:55
    Java 10改进了对Docker容器的支持 2018.4.9 版权声明:本文为博主chszs的原创文章,未经博主允许...但是在Docker容器中运行Java应用程序一直存在一个问题,那就是在容器中运行JVM程序在设置内存大小和CPU使用率...
  • 尽管应用程序的可移植性(即能够在任何Linux主机上运行相同的应用程序)仍然是采用Linux容器的主要驱动力,但另一个关键优势是能够优化服务器利用,以便您可以使用每一个计算。 当然,对于PROD之类的上游环境,您...
  • 可能是因为程序中有不良的代码,引致申请了的内存不能在使用后被释放. 可能因为程序结构不善,引致websphere容器花费大量开销用于优化造成. 这里有一点要特别注意,内存溢出的时候,通常的表现是...
  • AppDynamics Docker Monitoring Extension监视并报告各种指标,例如:容器总数,正在运行的容器,图像,CPU使用率,内存使用率,网络流量等。 AppDynamics Docker监控扩展使用Unix Socket或...
  • yarn Container 容器的优化 我遇到了一个问题: 由于服务器是三台4核16G的...yarn资源管理有虚拟核和虚拟内存的概念,可以将虚拟核、虚拟内存调整为物理机的两到三倍,增加任务数量,提高cpu使用率。 yarn.nodem...
  • 监控容器的资源使用 内存,cpu,io及网络 日志监控 监控整个业务系统的系统调用链路 自动扩容 当服务的使用资源达到指定的指标后自动新增一个容器,部署相关服务注册到注册中心后用来消除高并发带来的系统的压力 ...
  • 在这篇简短文章中,我将展示一种简单方法来确保在Docker容器中运行ADF应用程序在内存利用方面是健康Java应用程序。 我将使用标准工具JConsole,它是计算机上JDK安装一部分。 如果存在问题(例如,内存...
  • 如果我们仅在单个线程上运行所有内容,那么我们可能会发现CPU利用较低,并且觉得我们在浪费金钱来浪费未充分使用的计算机正常运行时间。 但是,当您运行带有Java应用程序之类的容器时,如何计算最终要使用的...
  • 查询容器信息 docker inspcet 查询信息,包括运行情况、存贮位置、配置参数、网络设置等。 查询容器的运行状态 docker inspect -f {{.State.Status}} 【容器】 ...如CPU使用率内存、网络、磁盘开销 容
  • 在最近一次开发过程中遇见这个这样问题:在面临突发性大量用户涌进系统时候,服务器资源使用并不高,cpu使用率28%左右,内存的使用率40%左右但是依然会造成系统卡顿,请求超时等问题。百思不得其解头发都快抓...
  • 在Docker中运行了一个多线程程序,容器启动时未对CPU和内存做任何限制,但是发现该程序只使用host上CPUcore0核心,cpu使用率无法突破100%,从而导致无法发挥多线程性能。不知道是什么原因,在何处被限制了,请...
  • Docker容器监控

    2019-12-19 19:28:42
    利用docker compose组合应用并利用scale可以快速对容器进行扩充,而docker compose启动的服务容器都在同一台宿主机上,对于一个宿主机上运行多个容器应用时,容器的运行情况,如:CPU使用率,内存使用率,网络状态,...
  • 在 TKE 上利用 HPA 实现业务弹性伸缩概述Kubernetes Pod 水平自动扩缩(Horizontal Pod Autoscaler,以下简称 HPA)可以基于 CPU 利用内存利用和其他自定义度量指标自动扩缩 Pod 副本数量,以使得工作负载...
  • c++容器

    2017-04-07 12:57:25
    stl:标准模板库 stl包括两方面:容器和算法 容器:是一种装数据的类型 ...使用容器的优点:当自己写一个链表、队列、数组时,既要花时间还要操心去维护,怕里面的内存不够或长度太短问题,使用容器就能解决这种问

空空如也

空空如也

1 2 3 4 5 ... 18
收藏数 357
精华内容 142
关键字:

容器的内存使用率