线上一个字体工具引发的内存泄漏问题

/ 后端 / 没有评论 / 390浏览

这个是怎么发现的呢,虽然没有引发OOM,但是自从我接手这个项目以后,前期都是改bug,写新需求,最近疫情在家,并且新做的小程序等待审核的期间,发现了个线上问题;发现每隔一段时间,一般是一两周,线上的堆内存就逐渐增大,虽然很缓慢的增长,但是使用jmap -heap命令查看到老年代也是越来越大,以往的gc没有起作用,正常来说内存占用应该是从部署逐步上升到保持一个稳定的水平。

1.使用命令获取jvm内存dump:

jmap -dump:format=b,file=10339.hprof 10339

 2.使用工具自动分析大概率有问题的内存占用线程及实例:

 大概看到了是这个DIsposer中的Hashtable占用了很大的内存;

3.查看按照内存占用降序的实例数:

 其实现在通过其中的内容已经看到了一些端倪,就是TrueTypeFont的其中字符串变量是HYHeiFangW,这个是以前开发人员封装的字体工具中使用到的;

 4.查看代码:

static  Font registerFont(String fontFileName,int style, float fontSize){
        if(fontFileName == null){
            fontFileName = HYHFW;
        }
    <span class="hljs-type">Font</span> <span class="hljs-variable">font</span> <span class="hljs-operator">=</span> <span class="hljs-literal">null</span>;
    <span class="hljs-keyword">try</span> {
        <span class="hljs-type">URL</span> <span class="hljs-variable">url</span> <span class="hljs-operator">=</span>  AbstractPenAndBackground.class.getClassLoader().getResource(fontFileName);
        <span class="hljs-type">File</span> <span class="hljs-variable">fontFile</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">File</span>(url.getFile());
        font = Font.createFont(Font.TRUETYPE_FONT, fontFile).deriveFont(style,fontSize);
        <span class="hljs-comment">//GraphicsEnvironment ge = GraphicsEnvironment .getLocalGraphicsEnvironment();</span>
        <span class="hljs-comment">//ge.registerFont(font);</span>
    } <span class="hljs-keyword">catch</span> (FontFormatException e) {
        e.printStackTrace();
    } <span class="hljs-keyword">catch</span> (IOException e) {
        e.printStackTrace();
    }

    <span class="hljs-keyword">return</span> font;
}</code></pre>

注释的代码就是引发内存泄漏的地方,跟进去代码可以看到:

sun.font.SunFontManager#registerFont方法:

 AppContext var8 = AppContext.getAppContext();
                        var6 = (Hashtable)var8.get(regFamilyKey);
                        var7 = (Hashtable)var8.get(regFullNameKey);
                        if (var6 == null) {
                            var6 = new Hashtable();
                            var7 = new Hashtable();
                            var8.put(regFamilyKey, var6);
                            var8.put(regFullNameKey, var7);
                        }

                        this.fontsAreRegisteredPerAppContext = true;
                    }

                    Font2D var12 = FontUtilities.getFont2D(var1);
                    int var9 = var12.getStyle();
                    FontFamily var10 = (FontFamily)var6.get(var4);
                    if (var10 == null) {
                        var10 = new FontFamily(var1.getFamily(var3));
                        var6.put(var4, var10);
                    }

每次调用字体工具方法,都会将新的Font实例放到静态Hashtable中;解决方法其实就是要么取消这个操作,要么将Font也单例化;