`
jht5945
  • 浏览: 4257 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

一次Font.createFont引起的Native Memory OOM的故障排查

 
阅读更多

周二(2011年12月13日)中午,服务器出现问题,新上的打水印功能出现不可用情况,有大量用户投诉过来。

于是我被从晋升答辩评委席拖回来处理故障。

 

一看到这个故障我就傻眼了,发现用户打水印时好时坏,于是我自己也做了下测试,发现程序功能没有问题,但还是陆续有用户报告故障上来。

 

跟进问题发现,服务器 “/tmp” 文件夹下大量生成 “+~JF343ABD3434343342323232.tmp” 的临时文件。

我的第一个反映就是这是什么呀,拉下来看看。

 

打开文件,看到几个字符 “方正大黑” ,这不是我们新加的字体么?

看了下程序源代码:

InputStream inputSteam = ClassName.class.getResourceAsStream("/resource/FONT_NAME.TTF");

Font font = Font.createFont(Font.TRUE_TYPE_FONT, inputStream);

查看方法 “java.awt.Font.createFont(ILjava.io.InputStrem;)Ljava.awt.Font;” 的源代码,看到这行代码就恍然大悟了:

return File.createTempFile("+~JF", ".tmp", null);

原来通过流创建字体时会创建临时文件,而且这个临时文件有2.1M之大,打了上千个水印临时文件夹就耗尽了,所以这时通过脚本不断去删除临时文件夹的文件,然后准备紧急发布将File.createFont创建对象改为静态(static)来解决这个问题。

 

但是到下午3点多有服务器Load飙高到100多,这时候重启应用并加入新机器后开始恢复正常。

服务器Load飙高非常不正常,想了好久都没想出来为什么,后来查看监控的信息,看到内存信息非常诡异:

 

看到这个图严重怀疑是Java的Native Memory内存泄漏,因为按照我们的配置,Java正常内存使用一般不会超过2.5G,而这里我们机器的内存使用了超过5G,而且还使用了Swap内存,所以这时也导致Load飙高。但为什么Java会使用这么多内存呢?

然后我从Sun(Oracle)的Bug Database中找到了这个Bug:

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7074159

上面的状态是“3-Accepted, bug”,说明的确是个BUG并且已经被确认。

从描述看和我们遇到的问题基本一样,然后把代码拿下来测试,惊奇的发现不会导致OOM。

这时果断放弃使用Mac来测试,将程序搞到我的Linux虚拟机上跑,这时内存果然涨到1G以上,OOM再现。

通过如下测试程序,可以还原Java Native OOM问题:

import java.awt.Font;

import java.awt.Graphics2D;

import java.awt.geom.AffineTransform;

import java.awt.image.BufferedImage;

import java.io.File;

 

publicclass TestTrueTypeFontLoad {

  publicstaticvoid main(String args[]) {

    try {

      for(int i = 0; i < 50000; i++) {

        Font afont = Font.createFont(Font.PLAIN, new File("arial.ttf"));

        BufferedImage bimg = new BufferedImage(100, 200, BufferedImage.TYPE_INT_ARGB);

        Graphics2D gs = bimg.createGraphics();

        gs.setFont(afont.deriveFont(Font.PLAIN, new AffineTransform(10, 0, 0, 10, 10, 10)));

        gs.drawString("This is font load test", 0, 0);

      }

    }

    catch (Exception e) {

      e.printStackTrace();

    }

  } 

}

通过把openjdk-6中的源代码拿过来编译,然后在运行代码前加上配置 “-Dsun.boot.class.path=” 加上刚才编译出来的类 ,再通过命令 “java -verbose:class” 打印出加载的类,看到JVM加载的类的确是我编译的类。然后通过不断修改源文件打印日志并监控Java占用内存情况。

 

终于排查出来,sun.font.Font2D会将FontStrike对象缓存到内存中,默认使用SoftReference在ConcurrentHashMap中保存对其的引用(可以通过sun.java2d.font.reftype来配置使用SoftReference或WeakReference,默认则使用SoftReference,在类sun.font.StrikeCache中),而FontStrike则包含了对Native Memory内存的引用,而通过下面的函数来释放这些Native Memory:

sun.font.StrikeCache.disposeStrike(sun.font.FontStrikeDisposer)V

 

于是果断在这个函数内加上System.out.println日志,但没有看到这个函数被调用,觉得非常奇怪,这个对象怎么没有被释放呢,而且他Hold的可是Native Memory啊。

于是,将sun.font.Font2D的 getStrike(Lsun.font.FontStrikeDesc;J)Lsun.font.FontStrike; 函数中的 “lastFontStrike = new SoftReference(strike);” 修改为 “lastFontStrike = new WeakReference(strike);”;在启动参数上加上 “-Dsun.java2d.font.reftype=weak”,且在程序中加入“System.gc();” 触发JVM的FULL GC,再次观察内存情况,发现程序内存稳定在500M左右,到此故障排查结束。 

 

关于Reference,大家可以Google一下“WeakReference vs SoftReference”。

可以看到WeakReference是在做Full GC时会回收,但SoftReference只在OOME前回收内存。

 

Java内存和JVM Native内存的关系如下图表示:

 

  • 大小: 10.3 KB
  • 大小: 42.3 KB
0
1
分享到:
评论

相关推荐

    Createfont.txt

    //另外,裁切之后,每个sprite的名字的最后一个字符对应了ascii码的编码,比如: //0: 我们只要将sprite的名字命名成xxx0,就可以了! //由于使用到的了sprite加载,所以字体图片请放在Resources目录下面,等...

    SIMYOU.TTF

    3. 创建一个Font对象,设置字体、大小和样式: ```java Font font = new Font(baseFont, 12, Font.NORMAL); ``` 4. 在创建PDF文档时,将这个Font对象应用到文本内容上: ```java Document document = new Document()...

    iText生成pdf解决中文不显示

    pdf凉字不显示,由于生成iText插件生成pdf的时候中文会显示不出来,遇到过的是"凉"字,查到是字体库的原因,网上下载字体库msyh.ttc,生成的时候指定字体库,就可以解决了,小bug一枚。注意有个bug,获取字库路径时...

    往图片写汉字显示乱码

    Font font = Font.createFont(Font.TRUETYPE_FONT, fontStream); font = font.deriveFont(18f); // 设置字体大小 g2d.setFont(font); ``` 这里我们先通过`FileInputStream`读取字体文件,然后用`Font.createFont()`...

    itextpdf中文依赖包

    在Java开发中,处理PDF文档是一项常见的任务,iTextPDF是一个广泛使用的开源库,它提供了丰富的API来创建、修改和操作PDF文档。在处理中文字符时,我们需要引入特定的依赖包,以确保中文文字能够正确显示。本文将...

    itext7中文输出打包

    FontProgram font = FontProgramFactory.createFont("path/to/simhei.ttf"); ``` 2. **创建PdfDocument和Document对象**:在初始化`PdfDocument`和`Document`对象时,你需要设置字体映射,这样iText7就知道如何...

    itext-asian-5.2.0.jar、itextpdf-5.5.5.jar 两个JAR包

    在IT行业中,PDF(Portable Document Format)是一种广泛用于文档交换的标准格式,因为它能保持文档的原始格式和布局,无论在哪个设备上查看。然而,在处理包含中文字符的PDF时,可能会遇到显示问题,这通常与字体...

    Java生成pdf文件,解决中文乱码问题.pdf

    * 设置中文字体,使用 BaseFont.createFont 方法创建一个字体对象 * 创建一个 Font 对象,用于设置字体样式 * 打开文档,并将要写入的内容添加到文档中 4. 解决中文乱码问题 在生成 PDF 文件时,解决中文乱码问题...

    iText PDF中文字体文件.rar

    2. **创建字体对象**:通过`FontFactory.createFont()`方法创建字体对象,指定字体名称和大小。例如,创建12号的宋体: ```java Font font = FontFactory.getFont("simsun", BaseFont.IDENTITY_H, BaseFont....

    java word转pdf源码示例

    首先,我们需要了解Apache POI,它是一个强大的API,用于读取、写入和修改Microsoft Office文件格式。主要组件包括HPSF(Hierarchical Property Set Facility)用于处理属性,HWPF(Horrible Word Processor Format...

    java 显示多种字体

    首先,Java中的`java.awt.Font`类是处理字体的核心类。它允许我们创建和管理字体对象,这些对象可以用于绘制文本,设置组件的字体样式等。创建一个新的`Font`对象通常需要三个参数:字体名、样式和大小。样式参数...

    zysong.ttf jfreechart 解决中文乱码 问题

    这通常通过`Font.createFont()`方法完成,它会返回一个Font对象。 ```java InputStream fontStream = new FileInputStream("path/to/zysong.ttf"); Font zysongFont = Font.createFont(Font.TRUETYPE_FONT, ...

    java随机动态生成汉字验证码图片

    Java提供了`java.awt.Font`类,可以通过`Font.createFont()`方法加载字体文件并调整大小。 3. **创建图片**:使用`java.awt.image.BufferedImage`类创建一个新的图片对象,设定其宽度和高度。验证码通常包含4-6个...

    iText7高级教程之构建基础块——第一章源代码工程

    iText7是一款强大的Java库,专门用于创建和编辑PDF文档。这个高级教程的"构建基础块"章节,显然是为了引导用户逐步理解并掌握如何利用iText7构建PDF文档的基本结构。在第一章中,我们通常会遇到如何初始化项目、设置...

    CreateFont函数生成工具

    1. 下载并运行CreateFont.exe工具。 2. 在界面上选择或输入所需的字体属性,如字体名称(如Arial)、大小、是否加粗、斜体、下划线等。 3. 预览所选字体效果,确保符合预期。 4. 点击“生成代码”按钮,工具会自动...

    POI中设置Excel单元格格式.docx

    HSSFFont font2 = wb.createFont(); font2.setFontName("仿宋_GB2312"); font2.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD); font2.setFontHeightInPoints((short) 12); setBorder.setFont(font); ``` 五、设置列宽 ...

    解决jfreechart中文乱码方案整理.doc

    Font font = Font.createFont(Font.TRUETYPE_FONT, fontStream).deriveFont(12f); renderer.setItemLabelFont(font); ``` #### 编码处理 确保所有涉及到中文的文件或流都采用统一的编码格式,通常推荐使用UTF-8...

    长城小标宋体_长城小标宋体_长城_

    Font customFont = Font.createFont(Font.TRUETYPE_FONT, fontStream); ``` 3. 创建并应用字体样式: ```java Font normal = customFont.deriveFont(Font.PLAIN, 14); // 设置字体大小和样式 GraphicsEnvironment ge...

Global site tag (gtag.js) - Google Analytics