周二(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
分享到:
相关推荐
//另外,裁切之后,每个sprite的名字的最后一个字符对应了ascii码的编码,比如: //0: 我们只要将sprite的名字命名成xxx0,就可以了! //由于使用到的了sprite加载,所以字体图片请放在Resources目录下面,等...
3. 创建一个Font对象,设置字体、大小和样式: ```java Font font = new Font(baseFont, 12, Font.NORMAL); ``` 4. 在创建PDF文档时,将这个Font对象应用到文本内容上: ```java Document document = new Document()...
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()`...
在Java开发中,处理PDF文档是一项常见的任务,iTextPDF是一个广泛使用的开源库,它提供了丰富的API来创建、修改和操作PDF文档。在处理中文字符时,我们需要引入特定的依赖包,以确保中文文字能够正确显示。本文将...
FontProgram font = FontProgramFactory.createFont("path/to/simhei.ttf"); ``` 2. **创建PdfDocument和Document对象**:在初始化`PdfDocument`和`Document`对象时,你需要设置字体映射,这样iText7就知道如何...
在IT行业中,PDF(Portable Document Format)是一种广泛用于文档交换的标准格式,因为它能保持文档的原始格式和布局,无论在哪个设备上查看。然而,在处理包含中文字符的PDF时,可能会遇到显示问题,这通常与字体...
* 设置中文字体,使用 BaseFont.createFont 方法创建一个字体对象 * 创建一个 Font 对象,用于设置字体样式 * 打开文档,并将要写入的内容添加到文档中 4. 解决中文乱码问题 在生成 PDF 文件时,解决中文乱码问题...
2. **创建字体对象**:通过`FontFactory.createFont()`方法创建字体对象,指定字体名称和大小。例如,创建12号的宋体: ```java Font font = FontFactory.getFont("simsun", BaseFont.IDENTITY_H, BaseFont....
首先,我们需要了解Apache POI,它是一个强大的API,用于读取、写入和修改Microsoft Office文件格式。主要组件包括HPSF(Hierarchical Property Set Facility)用于处理属性,HWPF(Horrible Word Processor Format...
首先,Java中的`java.awt.Font`类是处理字体的核心类。它允许我们创建和管理字体对象,这些对象可以用于绘制文本,设置组件的字体样式等。创建一个新的`Font`对象通常需要三个参数:字体名、样式和大小。样式参数...
这通常通过`Font.createFont()`方法完成,它会返回一个Font对象。 ```java InputStream fontStream = new FileInputStream("path/to/zysong.ttf"); Font zysongFont = Font.createFont(Font.TRUETYPE_FONT, ...
Java提供了`java.awt.Font`类,可以通过`Font.createFont()`方法加载字体文件并调整大小。 3. **创建图片**:使用`java.awt.image.BufferedImage`类创建一个新的图片对象,设定其宽度和高度。验证码通常包含4-6个...
iText7是一款强大的Java库,专门用于创建和编辑PDF文档。这个高级教程的"构建基础块"章节,显然是为了引导用户逐步理解并掌握如何利用iText7构建PDF文档的基本结构。在第一章中,我们通常会遇到如何初始化项目、设置...
1. 下载并运行CreateFont.exe工具。 2. 在界面上选择或输入所需的字体属性,如字体名称(如Arial)、大小、是否加粗、斜体、下划线等。 3. 预览所选字体效果,确保符合预期。 4. 点击“生成代码”按钮,工具会自动...
HSSFFont font2 = wb.createFont(); font2.setFontName("仿宋_GB2312"); font2.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD); font2.setFontHeightInPoints((short) 12); setBorder.setFont(font); ``` 五、设置列宽 ...
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...