1、问题描述

系统配置为单核4G, web 工程配置堆2G,  /tmp目录 二进制文件不断增加,平均一天增加20G, 手动清理/tmp目录,重启系统,问题依旧。

2、分析

/tmp 目录存放系统运行时产生的临时文件。在Redhat-like系统上,会定期清理/tmp目录下10天未访问的文件。这个机制保证了,linux不会像windows那样在较长时间运行后变得臃肿不堪。
 
清理脚本位于/etc/cron.daily/tmpwatch,内容如下,
 
#! /bin/sh
flags=-umc
/usr/sbin/tmpwatch "$flags" -x /tmp/.X11-unix -x /tmp/.XIM-unix \
     -x /tmp/.font-unix -x /tmp/.ICE-unix -x /tmp/.Test-unix \
     -X '/tmp/hsperfdata_*' 10d /tmp
/usr/sbin/tmpwatch "$flags" 30d /var/tmp
for d in /var/{cache/man,catman}/{cat?,X11R6/cat?,local/cat?}; do
    if [ -d "$d" ]; then
     /usr/sbin/tmpwatch "$flags" -f 30d "$d"
    fi
done
本质是调用了系统命令/usr/sbin/tmpwatch 来执行对/tmp和/var/tmp目录的清理。tmpwatch 一般被用来清扫那些用来临时驻留文件的目录。
 
注意到,tmpwatch删除时有个排除项,/tmp/hsperfdata_*,java程序在启动时,默认会生成/tmp/hsperfdata_{USERNAME}/{PID}的文件,
这个文件会存储jvm运行的相关信息。jps\jconsole等工具的数据源就是来自于这个文件。若这个文件不存在,jps命令执行时找不到这个进程。关于这个问题,曾经有个bug相关(https://bugzilla.redhat.com/show_bug.cgi?id=527425),这个bug就是由于tmpwatch 没有排除/tmp/hsperfdata_*这个目录,导致该目录被删除,jps无法找到对应的进程。
 
那么/tmp通常会有哪些临时数据在这里呢?
例如,jvm启动数据、mysql的sock文件、apache默认的上传文件目录、nginx的缓存文件以及一些其他进程的临时文件。
 
1.查看/tmp, 通过 ls /tmp | wc -l观察, /tmp文件大约以4个/s 的速度增加,而且都是二进制文件。

2.lsof -p pid 确定tmp文件都被进程id为10791的同一个Java进程打开。

根据上面的分析,这些文件应该是该进程的临时文件,而且不断在增加,有可能是文件句柄泄露。

查看该进程的句柄图

12.15号,系统打开的句柄数量在逐步的增加,而且没有出现相对平稳的迹象,确定是句柄泄露了,这印证了我们的猜想。

下来需要进一步分析究竟是什么原因造成的句柄泄露。

查看文件内容,vi -b /tmp/filename, 包含 <</Length 2541/Filter/FlateDecode>>stream 之类的内容。猜测应该是和解码有关。

谷歌搜索关键字,确定FlateDecode是解码 PDF stream 的一个工具。查看程序中引用相关pdf的代码,如下图所示:

public static byte[] transfer(byte[] bytes, int pageNum) throws IOException {
    LOG.info("PDF合同转IMAGE开始...pageNum={}", pageNum);
    PdfDecoder decode_pdf = new PdfDecoder(true);
    decode_pdf.scaling = 1.5F;
    FontMappings.setFontReplacements();
    byte[] outbytes = new byte[0];
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    try {
        decode_pdf.openPdfArray(bytes); //bytes is byte[] array with PDF
        BufferedImage img = decode_pdf.getPageAsImage(pageNum);
        ImageIO.write(img, "jpg", out);
        outbytes = out.toByteArray();
        LOG.info("PDF合同转IMAGE成功...pageNum={}", pageNum);
    catch (Exception e) {
        LOG.error("PDF合同转IMAGE异常...pageNum={},e={}", pageNum, e);
    finally {
        out.close();
    }
    return outbytes;
}

这段代码用来将pdf转化成一个jpg的图片,使用了jpedal第三方库。

jpedal是一个开源的纯Java的PDF文档解析库,可以用来方便的查看和编辑文字和图片。

回到代码, 按照以往编码的经验,有可能是PdfDecoder没有释放资源,导致生成的临时文件一直没有释放掉。查看jpedal文档,发现的确提供了closePdfFile 关闭pdf文件的方法。

finally 块里添加

decode_pdf.flushObjectValues(true);
decode_pdf.closePdfFile();

重新发版,发现之后句柄图达到了相对平稳的状态,tmp目录也不再继续增加临时文件。

虽然问题解决了,但是还有一些困惑。临时文件怎么生成的?page fault为啥这么多?

1、临时文件到底是怎么生成的?

decode_pdf.openPdfArray(bytes)  根据传进来的字节流 打开pdf文件,

jpedal在这里做了一个优化,当pdf文件小于16k时或者alwaysCacheInMemory = -1时,直接内存缓存该pdf。

当pdf文件的大小大于16k时,会在临时目录下生成一个前缀为page,后缀为bin的二进制文件,该临时目录由系统参数 java.io.tmpdir 指定,默认在/tmp目录下。

这样,由于线上环境的pdf基本都大于16k,所以/tmp目录下就会看到不断的临时文件生成。这个临时文件命名规则为page***.bin。

2、添加closePdf文件之后,为啥问题就解决了呢?

closePdf会调用PdfReader的closePdfFile()方法,该方法根据缓存的临时文件名称删除该临时文件。

3、未关闭pdf文件,为啥会引起较多的page fault呢?

page fault 分为 minor page fault 和major page fault。

major page fault也称为hard page fault, 指需要访问的内存不在虚拟地址空间,也不在物理内存中,需要从慢速设备载入。从swap回到物理内存也是hard page fault。

minor page fault也称为soft page fault, 指需要访问的内存不在虚拟地址空间,但是在物理内存中,只需要MMU建立物理内存和虚拟地址空间的映射关系即可。 
(通常是多个进程访问同一个共享内存中的数据,可能某些进程还没有建立起映射关系,所以访问时会出现soft page fault)

正常情况下,系统也会有一些pagefault,如下图所示:

,所以pagefault和该问题没有直接关系。minflt表示从内存加载数据时每秒出现的小的错误数目,可以忽略。如果majflt较大,表示从磁盘载入内存页面,发生了swap,此时需要关注。

3、总结

我们详细的回顾了此次线上发生的问题,以及如何去定位,然后去解决问题的整个过程。

(1)问题发现,收到系统磁盘空间不足的报警。

(2)问题定位,先根据du确认是tmp目录增长过快的问题,然后根据lsof和进程句柄图确定是文件句柄泄露,再根据临时文件的文件内容,定位相关的源代码,查看源代码,确认是文件句柄资源没有正确释放。

(3)解决问题,查看api,确认是资源泄露的问题,修复代码上线。

 
另外,第一次写这类关于线上问题故障的文章,希望大家多多反馈。

线上系统/tmp 目录不断增长分析与总结的更多相关文章

  1. CentOS系统/tmp目录里面的文件默认保留多久

    一.CentOS系统/tmp目录里面的文件默认保留多久 CentOS6默认保留30天,CentOS7默认保留10天 一.CentOS7系统/tmp目录里面的文件默认保留多久 CentOS7默认保留10 ...

  2. 线上BUG:MySQL死锁分析实战

    原文链接:线上BUG:MySQL死锁分析实战 1 线上告警 我们不需要关注截图中得其他信息,只要能看到打印得org.springframework.dao.DeadlockLoserDataAcces ...

  3. 有关线上系统点击没有任何相应得问题思考,主要针对PC端应用程序

    1.问题得起因 前段时间,客户得某些机器上,点击应用系统得快捷方式,没有任何响应,不弹出程序主界面,也没有任何得报错提示,甚至程序得错误日志也没有任何输出. 当时,听说发生这种情况得时候,有点懵了,不 ...

  4. 线上redis服务内存异常分析。

    项目中,新增了一个统计功能,用来统计不同手机型号的每天访问pv,看了下redis2.6有个setbit的功能,于是打算尝尝鲜把 redis从2.4更新到了2.6 因为是租了vps.服务器的内存只有4g ...

  5. Linux线上系统程序debug思路及方法

    http://blog.csdn.net/wangzuxi/article/details/44766221

  6. HBase工程师线上工作经验总结----HBase常见问题及分析

    阅读本文可以带着下面问题:1.HBase遇到问题,可以从几方面解决问题?2.HBase个别请求为什么很慢?你认为是什么原因?3.客户端读写请求为什么大量出错?该从哪方面来分析?4.大量服务端excep ...

  7. (转)HBase工程师线上工作经验总结----HBase常见问题及分析

    阅读本文可以带着下面问题:1.HBase遇到问题,可以从几方面解决问题?2.HBase个别请求为什么很慢?你认为是什么原因?3.客户端读写请求为什么大量出错?该从哪方面来分析?4.大量服务端excep ...

  8. 排查Java线上服务故障的方法和实例分析

    前言 作为在线系统负责人或者是一个技术专家,你可能刚刚接手一个项目就需要处理紧急故障,或者被要求帮忙处理一些紧急的故障,这个时候的情景是: (1)你可能对这个业务仅仅是听说过,而不怎么真正了解: (2 ...

  9. erlang 线上分析工具集锦

    1.Recon-Erlang线上系统诊断工具(引自): Erlang系统素以稳定可靠闻名,但是它也是c实现的,也是要管理比如内存,锁等等复杂的事情,也会出现Crash,而且crash的时候大部分原因是 ...

随机推荐

  1. 使用Myeclipse为数据表创建hibernate实体对象

    hibernate是orm框架的一种,orm即Object Relational Mapping,对象映射关系,其主要作用是将数据库(mysql,mssql,oracle)的对象转换为具体编程语言(如 ...

  2. 分布式系统的消息&服务模式简单总结

    分布式系统的消息&服务模式简单总结 在一个分布式系统中,有各种消息的处理,有各种服务模式,有同步异步,有高并发问题甚至应对高并发问题的Actor编程模型,本文尝试对这些问题做一个简单思考和总结 ...

  3. 使用Flink时遇到的问题(不断更新中)

    1.启动不起来 查看JobManager日志: WARN org.apache.flink.runtime.webmonitor.JobManagerRetriever - Failed to ret ...

  4. qt调用simsimi api实现小黄鸡

    项目地址:https://github.com/racaljk/xiaojianji 好吧我把它命名为小贱鸡.,下面说一说他的实现. 由于官方的simsimi api需要收费,免费试用版有各种限制,所 ...

  5. Windows编程之进程遍历(C++实现)

    Windows编程之进程遍历 PS: 主要扣代码使用,直接滑动到最下面使用. 遍历进程需要几个API,和一个结构体 1.创建进程快照 2.遍历首次进程 3.继续下次遍历 4.进程信息结构体 API 分 ...

  6. Java8函数之旅 (五) -- Java8中的排序

    前言    对数据进行排序是平常经常会用到的操作之一,使用Jav8排序可以减少你在排序这方面的代码量,优化你的代码. 测试用例代码 定义个实体类User,拥有姓名name,年龄age,积分credit ...

  7. zepto的返回顶部scrollTop的动画解决方法

    写移动端的时候,引入的zepto.js里的animate不包括scrollTop,所以返回顶部的时候,没有动画的效果.这里我使用的是setInterval的方法.代码详情如下 <!DOCTYPE ...

  8. python科学计算_scipy_常数与优化

    scipy在numpy的基础上提供了众多的数学.科学以及工程计算中常用的模块:是强大的数值计算库: 1. 常数和特殊函数 scipy的constants模块包含了众多的物理常数: import sci ...

  9. springMVC(6)---处理模型数据

    springMVC(6)---处理模型数据 之前一篇博客,写个怎么获取前段数据:springMVC(2)---获取前段数据,这篇文章写怎么从后端往前端传入数据. 模型数据类型             ...

  10. linux网路编程:字节序(大端、小端、网络、主机)

    字节序:就是数据在内存中的存放顺序,也可称之为端模式. 大端模式和小端模式的定义 1) Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端. 2) Big-End ...