美文网首页
递归撑爆内存的一个bug

递归撑爆内存的一个bug

作者: 黄云斌huangyunbin | 来源:发表于2018-04-13 18:26 被阅读0次

现象: 系统运行一段时间后,连续n次fullgc,然后系统挂了


突破口: 发现最后的n次full gc 过程中一次ygc都没有,情况有两种,

  • 申请的是大对象,直接去old区分配,没有ygc。

  • 申请的是class,直接去perm区,没有ygc。但是perm的大小很小,这个排除


image.png

那是什么代码产生的大对象呢:

查看报错时候的堆栈:


Exception in thread "main"java.lang.OutOfMemoryError: GC overhead limit exceeded

    at java.util.Arrays.copyOf(Arrays.java:3332)

    at java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:137)

    at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:121)

    at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:421)

    at java.lang.StringBuilder.append(StringBuilder.java:136)

    at com.yunbin.PinyinUtil.DoExchangeHead(PinyinUtil.java:277)

    at com.yunbin.PinyinUtil.DoExchangeHead(PinyinUtil.java:303)

    at com.yunbin.PinyinUtil.DoExchangeHead(PinyinUtil.java:303)

    at com.yunbin.PinyinUtil.DoExchangeHead(PinyinUtil.java:303)

    at com.yunbin.PinyinUtil.DoExchangeHead(PinyinUtil.java:303)

    at com.yunbin.PinyinUtil.DoExchangeHead(PinyinUtil.java:303)

    at com.yunbin.PinyinUtil.DoExchangeHead(PinyinUtil.java:303)

    at com.yunbin.PinyinUtil.DoExchangeHead(PinyinUtil.java:303)

    at com.yunbin.PinyinUtil.DoExchangeHead(PinyinUtil.java:303)

    at com.yunbin.PinyinUtil.DoExchangeHead(PinyinUtil.java:303)

    at com.yunbin.PinyinUtil.DoExchangeHead(PinyinUtil.java:303)

    at com.yunbin.PinyinUtil.DoExchangeHead(PinyinUtil.java:303)

    at com.yunbin.PinyinUtil.DoExchangeHead(PinyinUtil.java:303)

    at com.yunbin.PinyinUtil.DoExchangeHead(PinyinUtil.java:303)

    at com.yunbin.PinyinUtil.DoExchangeHead(PinyinUtil.java:303)

    at com.yunbin.PinyinUtil.DoExchangeHead(PinyinUtil.java:303)

    at com.yunbin.PinyinUtil.DoExchangeHead(PinyinUtil.java:303)

    at com.yunbin.PinyinUtil.DoExchangeHead(PinyinUtil.java:303)

    at com.yunbin.PinyinUtil.DoExchangeHead(PinyinUtil.java:303)

    at com.yunbin.PinyinUtil.DoExchangeHead(PinyinUtil.java:303)

    at com.yunbin.PinyinUtil.ExchangeHead(PinyinUtil.java:206)

    at com.yunbin.PinyinUtil.getPinyin(PinyinUtil.java:181)

    at com.yunbin.PinyinUtil.main(PinyinUtil.java:316)

看上去其实就比较明显是个递归造成的了。

查看代码,有个地方会造成内存放大:

image
原因:

多音字的时候,代码会做一个笛卡尔积,n个多音字就会有2的n次方倍,非常夸张。

代码很短,就全部贴出来了:

<dependency>
 <groupId>com.belerweb</groupId>
 <artifactId>pinyin4j</artifactId>
 <version>2.5.1</version>
</dependency>
package com.yy.datamine.utils.string;

import java.util.HashSet;
import java.util.Set;

import net.sourceforge.pinyin4j.PinyinHelper;
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType;
import net.sourceforge.pinyin4j.format.HanyuPinyinVCharType;
import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;

public class PinyinUtil {
    
    static HanyuPinyinOutputFormat PINYIN_OUTPUT = new HanyuPinyinOutputFormat();
    static {
        
        PINYIN_OUTPUT.setCaseType(HanyuPinyinCaseType.LOWERCASE);
        PINYIN_OUTPUT.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
        PINYIN_OUTPUT.setVCharType(HanyuPinyinVCharType.WITH_V);
    }
    
    public static String toShortPinyin(String text) {
        if (text == null)
            return text;
        if (text.isEmpty())
            return text;
        
        StringBuilder ret = new StringBuilder(text.length());
        for (int i = 0; i < text.length(); i++) {
            char c = text.charAt(i);
            
            String[] pinyin = null;
            try {
                pinyin = PinyinHelper
                        .toHanyuPinyinStringArray(c, PINYIN_OUTPUT);
            } catch (BadHanyuPinyinOutputFormatCombination e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            if (pinyin == null) {
                ret.append(c);
            } else {
                ret.append(pinyin[0].charAt(0));
            }
        }
        return ret.toString().toLowerCase();
    }
    
    public static String toPinyin(String text) {
        if (text == null)
            return text;
        if (text.isEmpty())
            return text;
        
        StringBuilder ret = new StringBuilder(text.length() * 3);
        for (int i = 0; i < text.length(); i++) {
            char c = text.charAt(i);
            
            String[] pinyin = null;
            try {
                pinyin = PinyinHelper
                        .toHanyuPinyinStringArray(c, PINYIN_OUTPUT);
            } catch (BadHanyuPinyinOutputFormatCombination e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            if (pinyin == null) {
                ret.append(c);
            } else {
                ret.append(pinyin[0]);
            }
        }
        return ret.toString().toLowerCase();
    }
    
    /**
     * 字符串集合转换字符串(逗号分隔)
     *
     * @param stringSet
     * @return
     */
    public static String makeAllKey(Set<String> stringSet) {
        StringBuilder str = new StringBuilder();
        int i = 0;
        for (String s : stringSet) {
            if (i == stringSet.size() - 1) {
                str.append(s);
            } else {
                str.append(s + ",");
            }
            i++;
        }
        return str.toString().toLowerCase();
    }
    
    /**
     * 字符串集合获取第一个拼音
     *
     * @param stringSet
     * @return
     */
    public static String makeSingleKey(Set<String> stringSet) {
        int i = 0;
        if (stringSet == null) {
            return "";
        }
        for (String s : stringSet) {
            //if (i == stringSet.size() - 1) {
            return s.toLowerCase();
            //}
            //i++;
        }
        return "";
    }
    
    private static boolean isChinese(char c) {
        Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);
        if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
                || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS
                || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
                || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B
                || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION
                || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS
                || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION) {
            return true;
        }
        return false;
    }
    
    /**
     * 获取拼音集合
     *
     * @param src
     * @param head
     *            是否返回首字母
     * @return Set<String> he
     */
    public static Set<String> getPinyin(String src, boolean head) {
        char[] srcChar = src.toCharArray();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < srcChar.length; i++) {
            if (isChinese(srcChar[i])) {
                sb.append(srcChar[i]);
            }
        }
        String str = sb.toString();
        if (str != null && !str.trim().equalsIgnoreCase("")) {
            
            srcChar = str.toCharArray();
            // 汉语拼音格式输出类
            HanyuPinyinOutputFormat hanYuPinOutputFormat = new HanyuPinyinOutputFormat();
            
            // 输出设置,大小写,音标方式等
            hanYuPinOutputFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);
            hanYuPinOutputFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
            hanYuPinOutputFormat.setVCharType(HanyuPinyinVCharType.WITH_V);
            
            String[][] temp = new String[str.length()][];
            for (int i = 0; i < srcChar.length; i++) {
                char c = srcChar[i];
                if (!isChinese(c)) {
                    continue;
                }
                // 是中文或者a-z或者A-Z转换拼音(我的需求,是保留中文或者a-z或者A-Z)
                if (String.valueOf(c).matches("[\\u4E00-\\u9FA5]+")) {
                    try {
                        temp[i] = PinyinHelper.toHanyuPinyinStringArray(
                                srcChar[i], hanYuPinOutputFormat);
                    } catch (BadHanyuPinyinOutputFormatCombination e) {
                        e.printStackTrace();
                    }
                } else if (((int) c >= 65 && (int) c <= 90)
                        || ((int) c >= 97 && (int) c <= 122)) {
                    temp[i] = new String[] { String.valueOf(srcChar[i]) };
                } else {
                    temp[i] = new String[] { String.valueOf(srcChar[i]) };
                }
            }
            if (head) {
                String[] pingyinArray = ExchangeHead(temp);
                Set<String> pinyinSet = new HashSet<String>();
                for (int i = 0; i < pingyinArray.length; i++) {
                    pinyinSet.add(pingyinArray[i]);
                }
                return pinyinSet;
            } else {
                String[] pingyinArray = Exchange(temp);
                Set<String> pinyinSet = new HashSet<String>();
                for (int i = 0; i < pingyinArray.length; i++) {
                    pinyinSet.add(pingyinArray[i]);
                }
                return pinyinSet;
            }
        }
        return null;
    }
    
    /**
     * 递归字首
     *
     * @param strJaggedArray
     * @return
     */
    public static String[] ExchangeHead(String[][] strJaggedArray) {
        String[][] temp = DoExchangeHead(strJaggedArray, true);
        return temp[0];
    }
    
    /**
     * 递归
     *
     * @param strJaggedArray
     * @return
     */
    public static String[] Exchange(String[][] strJaggedArray) {
        String[][] temp = DoExchange(strJaggedArray);
        return temp[0];
    }
    
    /**
     * 递归
     *
     * @param strJaggedArray
     * @return
     */
    private static String[][] DoExchange(String[][] strJaggedArray) {
        int len = strJaggedArray.length;
        if (len >= 2) {
            int len1 = strJaggedArray[0].length;
            int len2 = strJaggedArray[1].length;
            int newlen = len1 * len2;
            String[] temp = new String[newlen];
            int Index = 0;
            for (int i = 0; i < len1; i++) {
                for (int j = 0; j < len2; j++) {
                    temp[Index] = strJaggedArray[0][i] + strJaggedArray[1][j];
                    Index++;
                }
            }
            String[][] newArray = new String[len - 1][];
            for (int i = 2; i < len; i++) {
                newArray[i - 1] = strJaggedArray[i];
            }
            newArray[0] = temp;
            return DoExchange(newArray);
        } else {
            return strJaggedArray;
        }
    }
    
    /**
     * 递归字首
     *
     * @param strJaggedArray
     * @return
     */
    private static String[][] DoExchangeHead(String[][] strJaggedArray,
                                             boolean first) {
        int len = strJaggedArray.length;
        if (len >= 2) {
            int len1 = strJaggedArray[0].length;
            int len2 = strJaggedArray[1].length;
            int newlen = len1 * len2;
            String[] temp = new String[newlen];
            int Index = 0;
            for (int i = 0; i < len1; i++) {
                for (int j = 0; j < len2; j++) {
                    if (first) {
                        temp[Index] = strJaggedArray[0][i].substring(0, 1)
                                + strJaggedArray[1][j].substring(0, 1);
                    } else {
                        if (strJaggedArray[1][j].length() > 0) {
                            temp[Index] = strJaggedArray[0][i]
                                    + strJaggedArray[1][j].substring(0, 1);
                        }
                    }
                    Index++;
                }
            }
            String[][] newArray = new String[len - 1][];
            for (int i = 2; i < len; i++) {
                newArray[i - 1] = strJaggedArray[i];
            }
            newArray[0] = temp;
            return DoExchangeHead(newArray, false);
        } else {
            String[][] newArray = new String[1][1];
            newArray[0][0] = strJaggedArray[0][0];
            return newArray;
        }
    }
    
    /**
     * @param args
     */
    public static void main(String[] args) {
        String str = "播播播播播播播播播播播播播播播播播播播播播播播播播播播播播播播播播";
        Set<String> headSet = getPinyin(str, true);
        System.out.println(makeSingleKey(getPinyin(str, true)));
        System.out.println(makeSingleKey(getPinyin(str, false)));
        
    }
    
}


相关文章

网友评论

      本文标题:递归撑爆内存的一个bug

      本文链接:https://www.haomeiwen.com/subject/xprhkftx.html