zoukankan      html  css  js  c++  java
  • java进程内存溢出案例

    一. 上节回顾

    1. 内存

    2. 场景一:磁盘和文件写案例

    3. 命令:vmstat

    二. 上节的两个问题

    问题一:buffer是磁盘读数据还是写数据的缓存?

    问题二:cache是对文件读数据的缓存,是不是也会缓存写文件的数据?

    问题一分析步骤:

    java进程内存溢出,问题定位以及分析(mat)

    1. 运行下面的命令,清理缓存,从文件/tmp/file中,读取数据写入空设备

    echo 3 > /proc/sys/vm/drop_caches
    dd if=/data/file of=/dev/null

    2. 使用vmstat 1查看内存输出

    观察vmstat的输出,会发现读取文件时,也就是bi大于0时,buff都是80,没有变化,而cache则在不停增长,可以得出什么结论?

    cache是从磁盘读取文件的页缓存,也就是用来缓存从文件读取的数据

    问题二分析步骤:

    那么磁盘读又是什么情况?

    1. 首先清理缓存,从磁盘分区/dev/vda1中读取数据,写入空设备

    echo 3 > /proc/sys/vm/drop_caches
    dd if=/dev/sda1 of=/dev/null bs=500M count=1024

    2. 使用vmstat 1查看内存输出

    观察vmstat的输出,会发现读磁盘时,也就是bi大于0时,buff和cache都在增长,但buff增长快了很多,说明了什么问题?

    说明读磁盘时,数据缓存到了buff中

    经过上一个场景中的案例分析:可以对比得到这样的结论:

    读文件时数据会缓存到cache中,而读磁盘时数据会缓存到buff中

    也可以通过案例,了解到:

    buff既可以用来将要写入磁盘数据的缓存,也可以用来缓存从磁盘读取数据的缓存

    cache既可以用来从文件读取数据的页缓存,也可以用来写文件的页缓存

    简单的总结:buff是对磁盘数据的缓存,而cache是对文件数据的缓存,它们既会用在读请求中,也会用在写请求中

    三. java进程内存溢出案例

    1. 内存溢出了,怎么定位?

    java导致CPU高的问题,OOM,导致了CPU上不去,一直在50%

    对进程来说,能够看到的其实是内核提供的虚拟内存,这些内存还需要通过页表,由系统映射为物理内存

    当进程通过malloc()申请虚拟内存后,系统不会立即为其分配物理内存,而是每次访问时,才通过缺页异常嵌入内核中分配内存

    备注:缺页异常:并不是每次CPU都能访问到相应的物理地址单位,因此这样映射失败了,就产生了缺页异常

    Linux中还会使用buff和cache,分别把文件和磁盘读写的数据缓存到内存中

    发生事故:

    (1) 没有正确回收分配的内存,导致了内存泄漏

    (2) 访问的是已分配内存边界外的地址,导致程序异常退出

    内存的分配和回收

    在前面讲进程的内存空间时,知道用户空间包含多个不同的内存段,比如:只读段、数据段、堆、栈以及文件映射,这些内存段正是应用程序使用内存的基本方式

    比如:在程序定义了一个局部变量,比如一个整数数组 int data[32],定义了一个可以存储32个整数的内存段,由于这是一个局部变量,它会从内存空间的栈中分配内存

    栈内存由系统自动分配和管理,一旦程序运行出现超过了这个局部变量的作用域,栈内存就会被系统自动回收,所以不会产生内存泄漏的问题

    只读段:包括代码和常量

    数据段:包括全局变量

    堆:包括动态分配的内存,从低地址开始向上增长

    文件映射段:包括动态库,共享内存,从高地址开始向下增长

    栈:包括局部变量和函数调用的上下文

    很多时候,事先并不知道数据的大小,所以就用到标准库函数malloc(),在程序中动态分配内存,这时候,系统就会从内存空间的堆中分配内存

    堆内存由应用程序自己分配和管理,除非程序退出,这些堆内存并不会被系统自动释放,而是需要应用程序明确调用库函数free()来是否它们,如果应用程序没有正确释放堆内存,就会造成内存泄漏

    那么其他内存段是否也会导致内存泄漏?

    只读段:包括程序的代码和常量,由于是只读的,不会再去分配新的内存,所以不会产生内存泄漏

    数据段:包括全局变量和静态变量,这些变量在定义时已经确定了大小,所以不会产生内存泄漏

    文件映射段:包括动态库和共享内存,其中共享内存由程序动态分配和管理,所以,如果程序在分配后忘记了回收,就会导致跟内存类似的泄漏问题

    内存泄漏是很严重的问题,如果出现了,不仅应用程序自己不能访问,系统也不能把内存再次分配给其他应用试验,内存泄漏不断积累,甚至整个系统的内存都被耗尽,机器也不能工作了

    系统可以通过OOM进程杀死进程,但是在OOM引发之前,会出现很多反应,比如:CPU占用很高,内存一直上升,访问整个系统会越来越慢

    2. 步骤

    (1) 把pertest.war包放在Tomcat的webapps下

    (2) vim catalina.sh,设置Java堆大小

    重启tomcat后使用ps -ef | grep java查看

    (3) 启动Tomcat,看到查看日志:tail -f catalina.out

    (4) 使用jmeter或其他工具,发起请求

    (5) 用vmstat观察内存变化,每隔3s输出一组数据

    vmstat 3

    从输出结果来看,内存的free列在不停的变小,而buff和cache基本保持不变。未使用的内存在不断减小,而buff和cache基本不变,这说明系统中使用的内存一直在升高,但并不能说明内存泄漏,因为应用程序运行中需要的内存也可能会增大,比如程序中用了一个动态增长的数组来缓存计算结果,占用内存自然会增长

    那怎么确定是不是内存泄漏了?

    方法一:访问页面:http://192.168.0.109:8080/pertest/init1.jsp

    在页面出现了OOM

    Exception
    
    org.apache.jasper.JasperException: javax.servlet.ServletException: java.lang.OutOfMemoryError: Java heap space
        org.apache.jasper.servlet.JspServletWrapper.handleJspException(JspServletWrapper.java:604)
        org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:499)
        org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:385)
        org.apache.jasper.servlet.JspServlet.service(JspServlet.java:329)
        javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
        org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
    Root Cause
    
    javax.servlet.ServletException: java.lang.OutOfMemoryError: Java heap space
        org.apache.jasper.runtime.PageContextImpl.handlePageException(PageContextImpl.java:666)
        org.apache.jsp.init1_jsp._jspService(init1_jsp.java:167)
        org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
        javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
        org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:476)
        org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:385)
        org.apache.jasper.servlet.JspServlet.service(JspServlet.java:329)
        javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
        org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)

    到这里输入以下命令

    jmap -F -dump:format=b,file=pertestdump1220.bin 27910   # pertestdump1220.bin为文件名,可以自己任意命名

    命令格式:

    jmap [option] <pid>

    -dump:生成Java堆栈的快照

    -F:当虚拟机进程对-dump选项没有响应时,可使用这个选项生成dump快照

    format=b:用二进制的数据生成

    file:生成快照的名称

    -histo:显示堆中对象统计信息,包括类,实例数量和合计容量

    -heap:线上Java堆详细信息,如使用哪种回收器,参数配置,分代(老年代、持久代、年轻代)

    方法二:输入top命令

    输入top命令,也可以看到use%的CPU占用90%以上,并且这个就是当前这个Java进程导致的

  • 相关阅读:
    BZOJ 3132: 上帝造题的七分钟 树状数组+差分
    PAT Advanced 1006 Sign In and Sign Out (25 分)
    PAT Advanced 1011 World Cup Betting (20 分)
    PAT Basic 1032 挖掘机技术哪家强 (20 分)
    PAT Basic 1028 人口普查 (20 分)
    PAT Basic 1004 成绩排名 (20 分)
    大数据数据库HBase(二)——搭建与JavaAPI
    PAT Advanced 1009 Product of Polynomials (25 分)(vector删除元素用的是erase)
    PAT Advanced 1002 A+B for Polynomials (25 分)(隐藏条件,多项式的系数不能为0)
    PAT Basic 1041 考试座位号 (15 分)
  • 原文地址:https://www.cnblogs.com/my_captain/p/12685244.html
Copyright © 2011-2022 走看看