java溢出 事件触发_java进程内存溢出案例

一. 上节回顾

1. 内存

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

3. 命令:vmstat

二. 上节的两个问题

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

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

问题一分析步骤:

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

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

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

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

1c69f506fef4343bdf7d656c0b8e093a.png

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

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

问题二分析步骤:

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

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

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

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

50edc8efee56db9a688264bd54a015c2.png

观察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堆大小

f6b1d0bcad89fc8da989c58eb0b919d9.png

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

8aaef7019c33c92548acce93702f1247.png

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

2e924905cc77f62ac514f5acf2e6d3d1.png

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

d674721f4d3579ef0477a6a882a4cee4.png

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

vmstat 3

48d21a19f5c9fb132e2bb2c67b15d613.png

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

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

在页面出现了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)

aad8c2b9b36dec7de2b3f5104c6a4fd6.png

到这里输入以下命令

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

命令格式:

jmap [option]

-dump:生成Java堆栈的快照

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

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

file:生成快照的名称

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

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

4555ff58f63616c87a436a9d246d55f1.png

方法二:输入top命令

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

c37449cd99c86f7d6779f0a5a033daa3.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值