记一次线上服务器内存溢出排查

首先,这台服务器是我们的打印服务器,使用的是finereport7.0自带的webreport程序

背景是自从发布了一个供应商包装打印的功能后打印服务器经常内存溢出

首先在配置层面在系统启动时需要加上参数:

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/yapp/gc_log/

在奔溃时拿到java_pid27893.hprof文件

在eclipse中安装MAT工具,并打开该hprof文件

记一次线上服务器内存溢出排查

可以看到WebappClassLoader加载了3.9G的对象,点击Dominator Tree按钮查看最多占用内存的对象信息

记一次线上服务器内存溢出排查

发现在finereport服务器的程序上叫BaseUtils的类下有个map的空间特别大,

查看源码

public class BaseUtils
{
  public static final String RESOURCE_ENCODER = "GBK";
  private static Map imageMap = new HashMap();
  
  public static BufferedImage readImageWithCache(String paramString)
  {
    return readImage(paramString, true);
  }
  
  private static BufferedImage readImage(String paramString, boolean paramBoolean)
  {
    Object localObject = null;
    if (paramBoolean) {
      localObject = imageMap.get(paramString);
    }
    if ((localObject instanceof BufferedImage)) {
      return (BufferedImage)localObject;
    }
    InputStream localInputStream = readResource(paramString);
    if (localInputStream == null) {
      return null;
    }
    BufferedImage localBufferedImage = null;
    try
    {
      localBufferedImage = readImage(localInputStream);
      localInputStream.close();
    }
    catch (IOException localIOException)
    {
      FRContext.getLogger().error(localIOException.getMessage(), localIOException);
    }
    if (paramBoolean) {//对图片进行缓存,并且不进行释放,从而导致报表中打印的图片越来越多的时候会越来越占内存
      imageMap.put(paramString, localBufferedImage);
    }
    return localBufferedImage;
  }

从MAT分析中看到,String参数表示图片的地址信息,BufferedImage对象则是图片字节流,比如有张图片的地址是http://bfile.srm.xxx.com/group1/M00/00/25/ClbY8Fyuzm2AAkXVAP5Nj6sa3E0749.png

打开显示是张彩色图片,另存为图片后有17MB大,因此如果用户持续使用改打印显示图片功能,并且图片数量不断累积的话是会导致越来越多的内存被占用并且得不到释放的

后查看fineReport的函数文档,对于在打印报表显示图片的功能来说有个toImage函数可加上false参数来指定不对打印报表中要显示的图片进行缓存,从而避免该问题的发生。

转载于:https://my.oschina.net/ffse54s/blog/3050247