记一次Font导致JVM堆外内存泄漏分析

栏目: Java · 发布时间: 5年前

内容简介:下面是问题代码的简化版本每次new Font()之后,调用g.drawString()方法都会在Non-Heap区域分配一块内存且不回收g.drawString()的调用栈如下,

下面是问题代码的简化版本

public class FontMain {

    public static void main(String[] args) throws IOException, FontFormatException, InterruptedException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        File file = new File("/Users/cayun/PingFang.ttc");
        while (true) {
            run(file);
            Thread.sleep(1);
        }
    }

    private static void run(File file) throws IOException, FontFormatException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        BufferedImage blankImage = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = blankImage.createGraphics();
        Font font = Font.createFont(Font.TRUETYPE_FONT, file);
        font = font.deriveFont(12.0f);
        g.setFont(font);
        g.drawString("hello", 12, 12);
    }
}
复制代码

泄漏原因

原因概述

每次new Font()之后,调用g.drawString()方法都会在Non-Heap区域分配一块内存且不回收

调用栈

g.drawString()的调用栈如下,

SunGraphics2D.drawString(String, int, int) -> ValidatePipe.drawString(SunGraphics2D, String, double, double) -> SunGraphics2D.getFontInfo() -> SunGraphics2D.checkFontInfo -> Font2D.getStrike(Font, AffineTransform, AffineTransform, int, int) -> Font2D.getStrike(FontStrikeDesc, boolean) -> FileFont.createStrike(FontStrikeDesc) -> ... -> T2KFontScaler.<init>(Font2D, int, boolean, int) -> T2KFontScaler.initNativeScaler(...)

根本原因

在调用栈中第二个标红的部分

new T2KFontScaler() 时会调用 T2KFontScaler.initNativeScaler()这个native方法,这个native方法会在Non-Heap部分分配内存,且之后也没有相应的回收机制。

demo代码&效果图

public class FontMain {

    public static void main(String[] args) throws IOException, FontFormatException, InterruptedException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, ClassNotFoundException, InstantiationException {
        File file = new File("/System/Library/Fonts/AquaKana.ttc");
        Font font = Font.createFont(Font.TRUETYPE_FONT, file);
        font = font.deriveFont(12.0f);
        BufferedImage blankImage = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = blankImage.createGraphics();
        g.setFont(font);

        // T2KFontScaler无法通过new的方式创建,此处使用反射创建
        Class clazz = Class.forName("sun.font.T2KFontScaler");
        Constructor constructor = clazz.getConstructor(Font2D.class, int.class, boolean.class, int.class);
        constructor.setAccessible(true);

        while (true) {
            constructor.newInstance(((SunGraphics2D) g).getFontInfo().font2D, 0, true, 80005872);
            Thread.sleep(1);
        }
    }
}
复制代码
记一次Font导致JVM堆外内存泄漏分析

辅助证明:JDK已知bug

JDK-7074159 : run out of memory

解决方案

为字体做个缓存

public class FontMain {
    private static Font font = null;
    private static Object lock = new Object();

    public static void main(String[] args) throws IOException, FontFormatException, InterruptedException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        File file = new File("/Users/cayun/PingFang.ttc");
        while (true) {
            run(file);
            Thread.sleep(1);
        }
    }

    private static void run(File file) throws IOException, FontFormatException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        BufferedImage blankImage = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = blankImage.createGraphics();
        if (font == null) {
            synchronized (lock) {
                if (font == null) {
                    font = Font.createFont(Font.TRUETYPE_FONT, file);
                }
            }
        }
        font = font.deriveFont(12.0f);
        g.setFont(font);
        g.drawString("hello", 12, 12);
    }
}
复制代码

原因详解

这个解决方法看起来有点奇怪,或许很容易就会有这样一个疑问:明明导致内存泄漏的是g.drawString()方法,却为何要对Font做缓存?

为了简单说明原因,我们先定义两种方案

  1. 方案1: 不使用缓存,就是原先会导致内存泄漏的方案
  2. 方案2: 对字体做缓存

以上所述就是小编给大家介绍的《记一次Font导致JVM堆外内存泄漏分析》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

iOS Web应用开发

iOS Web应用开发

皮基 (Andrea Picchi) / 罗晴明 / 人民邮电出版社 / 2013-8-1 / CNY 79.00

本书介绍了如何使用Web标准技术来为iPhone和iPad制作Web应用。书中利用最前沿的Web和移动技术,演示了如何使用HTML5来完成繁重的基础工作,如何使用CSS3来制作外观,以及如何使用JavaScript来为移动网站或Web应用添加程序逻辑。 通过阅读本书,读者可以掌握面向移动的项目的开发流程。作者逐章递进,引导读者了解iOS设计与开发的各个步骤。读者可以学习到如下知识: 设......一起来看看 《iOS Web应用开发》 这本书的介绍吧!

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具