“java.lang.OutOfMemoryError:Java堆空间”在xml文件上调用FileUtils.readFileToString时

时间:2015-05-13 14:54:37

标签: java xml encoding heap fileutils

我的程序遍历一个目录并使用我的Recoder类中的以下函数在编码上使用FileUtils.readFileToString读取文件,并在另一个编码上使用FileUtils.write覆盖它。

然而,当它尝试使用xml文件执行此操作时,我在标题处获得异常,否则,它工作正常(我已经在.dava,.js,.css,.html ,. JSP ...)。

public class Recoder {
    private static Charset fromCharset;
    private static Charset toCharset;

    public static void recodeToUTF(File f, boolean verbose){
        try{
            if(verbose){
                System.out.println("Convertendo "+f.getAbsolutePath()+" para UTF-8");
            }
            toCharset = Charset.forName("UTF-8");
            String content = FileUtils.readFileToString(f);
            FileUtils.write(f,content, toCharset);
        }catch(Exception e){    
        }
    }

    public static void recodeFile(File f, String de, String para, boolean verbose){
        try{
            if(verbose){
                System.out.println("Convertendo "+f.getAbsolutePath()+" de "+ de + " para "+ para);
            }
//          CharsetDetector cd= new CharsetDetector();
//          FileInputStream fis = new FileInputStream(f);
//          cd.setText(fis);
//          fis.close();
//          CharsetMatch cm = cd.detect();

//          if(cm!=null){
//              fromCharset = Charset.forName(cm.getName());
//          }else{ fromCharset = Charset.forName(de);}
            fromCharset = Charset.forName(de);
            toCharset = Charset.forName(para);
            String content = FileUtils.readFileToString(f,fromCharset);
            FileUtils.write(f,content,toCharset);
            content=null;
        }catch(Exception e){    
        }
    }

    public static String removeAcentos(String str) {
        str = Normalizer.normalize(str, Normalizer.Form.NFD);
        str = str.replaceAll("[^\\p{ASCII}]", "");
        return str;  
    }

}

CharsetDetector的东西来自ICU4J,由于某种原因,它在调用cd.detect()时会挂起,所以,现在,我只是把它留在了评论中。

这是它的主要内容:

public static void main( String[] args ){
        DecoderArguments decArgs = new DecoderArguments();
        JCommander jc = new JCommander(decArgs, args);
        try {
            if(args.length>0){
                for(String s : decArgs.files){
                    File file;
                    if (decArgs.recursive){
                        System.out.println("Executando Recursivamente em: "+ s);
                        file = new File(s);
                        if(file.isDirectory()){
                            Collection<File> files = FileUtils.listFiles(file,FileFileFilter.FILE, DirectoryFileFilter.DIRECTORY);
                            for (File f : files){
                                boolean exec=true;
                                for(String excl : decArgs.excludes){
                                    if (f.getAbsolutePath().contains(excl)) exec=false;
                                }
                                if (file.exists() && exec){
                                    if("".equals(decArgs.fromEncoding)){
                                        Recoder.recodeToUTF(f, decArgs.verbose);
                                    }else {
                                        Recoder.recodeFile(f, decArgs.fromEncoding, decArgs.toEncoding, decArgs.verbose);
                                    }
                                    System.gc();
                                }                           
                            }
                        }else{
                            System.out.println("Por favor, informe um diretorio para ler recursivamente.\n"
                                    + "Uso: java -jar decoder.jar <-r> Caminho|Arquivo");
                        }       
                    }else{
                        System.out.println("Convertendo arquivo: "+ s);
                        file = new File(s);
                        boolean exec=true;
                        for(String excl : decArgs.excludes){
                            if (file.getAbsolutePath().contains(excl)) exec=false;
                        }
                        if (file.exists() && exec){
                            if("".equals(decArgs.fromEncoding)){
                                Recoder.recodeToUTF(file, decArgs.verbose);
                            }else {
                                Recoder.recodeFile(file, decArgs.fromEncoding, decArgs.toEncoding, decArgs.verbose);
                            }
                        }
                    }
                }
            }else if (args.length==0){
                System.out.println("Sintaxe incorreta.\n");
                jc.usage();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

注意事项:

  • 我在Eclipse上使用以下VM参数-Xms1024m -Xmx2048m
  • JDK版本1.7.0_75
  • 我已尝试过一些System.gc()但无效
  • 只有当main以递归方式运行时才会发生(decArgs.recursive=true),抛出异常的同一文件会在
  • 时顺利重新编码。
  • 已经检查了bash上的文件编码并硬编码进行测试,也没有运气。

关于为什么会发生这种情况的任何想法?

提前致谢!

修改 使用Recoder.recodeToUTF方法而不是Recoder.recodeFile会导致不抛出OutOfMemory。也许尝试使用错误的编码打开会导致内存泄漏。

分析生成的.hprof,(混乱的)300Mb xml文件正在使用大约500Mb的堆。但是,堆的最大大小设置为2Gb

1 个答案:

答案 0 :(得分:1)

您的记忆正在泄露,以便检查您可以解决的问题 使用这些参数-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/to/dump配置JVM。当OutOfMemoryError被抛出时,将在/path/to/dump中创建转储。然后,您可以使用Eclipse Memory Analyzer对其进行分析,并查找泄露您记忆的对象。 好教程here