Java堆空间内存不足:java.lang.OutOfMemoryError,没有更多的空间。

3

我不知道为什么会出现这个错误。我找不到任何方法来节省更多的内存。如果有人能帮助我使其更加高效,将不胜感激。

还有人可以检查我的代码,看看我是否做了什么傻事吗?提前致谢。

主类

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.SpringLayout;
import javax.swing.JTextField;
import javax.swing.JTextArea;
import javax.swing.JScrollPane;
import javax.swing.JButton;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

public class Main extends JFrame {

    private JPanel contentPane;
    private JTextField Searchq;
    private JScrollPane scrollPane;
    private static JTextArea txtpdisplay;

    /**
     * Launch the application.
     * 
     * @throws IOException
     */

    public static void filelistandfind(String find) throws IOException {
        Files.walk(Paths.get("C:\\Users\\localness\\Desktop\\Locker")).forEach(
                filePath -> {
                    if (Files.isRegularFile(filePath)) {
                        if (filePath.toString().endsWith(".docx")) {
                            try {
                                if (DocxCompair.Readfile(filePath.toString(),
                                        find)) {

                                    //System.out.println(filePath);
                                    txtpdisplay.append(filePath.toString() + "\n");
                                }
                            } catch (Exception e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            }

                        }
                    }
                });
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    Main frame = new Main();
                    frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * Create the frame.
     */
    public Main() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 646, 344);
        contentPane = new JPanel();
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        setContentPane(contentPane);
        SpringLayout sl_contentPane = new SpringLayout();
        contentPane.setLayout(sl_contentPane);

        Searchq = new JTextField();
        sl_contentPane.putConstraint(SpringLayout.NORTH, Searchq, 5,
                SpringLayout.NORTH, contentPane);
        sl_contentPane.putConstraint(SpringLayout.WEST, Searchq, 108,
                SpringLayout.WEST, contentPane);
        sl_contentPane.putConstraint(SpringLayout.SOUTH, Searchq, 36,
                SpringLayout.NORTH, contentPane);
        contentPane.add(Searchq);
        Searchq.setColumns(10);

        scrollPane = new JScrollPane();
        sl_contentPane.putConstraint(SpringLayout.NORTH, scrollPane, 3, SpringLayout.SOUTH, Searchq);
        sl_contentPane.putConstraint(SpringLayout.WEST, scrollPane, 5, SpringLayout.WEST, contentPane);
        sl_contentPane.putConstraint(SpringLayout.SOUTH, scrollPane, -9, SpringLayout.SOUTH, contentPane);
        sl_contentPane.putConstraint(SpringLayout.EAST, scrollPane, -5, SpringLayout.EAST, contentPane);
        sl_contentPane.putConstraint(SpringLayout.EAST, Searchq, 0,
                SpringLayout.EAST, scrollPane);
        contentPane.add(scrollPane);

        txtpdisplay = new JTextArea();
        txtpdisplay.setText("");
        scrollPane.setViewportView(txtpdisplay);

        JButton Search = new JButton("Search");
        Search.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent arg0) {
                try {
                    filelistandfind(Searchq.getText());
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        });
        sl_contentPane.putConstraint(SpringLayout.NORTH, Search, 2, SpringLayout.NORTH, Searchq);
        sl_contentPane.putConstraint(SpringLayout.WEST, Search, 0, SpringLayout.WEST, scrollPane);
        sl_contentPane.putConstraint(SpringLayout.SOUTH, Search, -3, SpringLayout.SOUTH, Searchq);
        sl_contentPane.putConstraint(SpringLayout.EAST, Search, -6, SpringLayout.WEST, Searchq);
        contentPane.add(Search);
    }
}

DocxCompair类

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Paths;
import java.util.zip.ZipInputStream;

public final class DocxCompair {
    private static byte[] buffer = new byte[204800];
    private static String result;
    private static int len;

    /*
     * public static void main(String[] args) throws IOException { String s =
     * "Stuart.docx"; String n = "<#tag>"; FileInputStream is = new
     * FileInputStream(s); ZipInputStream zis = new ZipInputStream(is);
     * System.out.println(Readfile(s, n)); }
     */
    private static boolean extractFile(ZipInputStream zipIn, String filePath,
            String text) throws IOException {
        result = "";
        len = 0;
        while ((len = zipIn.read(buffer)) > 0) {
            // System.out.print(new String(buffer));
            System.gc();
            result += result + new String(buffer);
        }
        // System.out.println();
        if (result.contains(text)) {
            return true;
        } else {
            return false;
        }
    }

    private static String texttag;
    private static String fileName;
    private static ZipInputStream zip;

    public static boolean Readfile(String names, String text)
            throws FileNotFoundException {

        // TODO Auto-generated method stub
        fileName = names;
        zip = new ZipInputStream(new FileInputStream(fileName));
        try {
            String name;
            while (true) {
                try {
                    name = zip.getNextEntry().getName();
                    // System.out.println(name);
                    if (name.startsWith("word/document.xml")) {
                        // System.out.println(name);
                        texttag = text;
                        texttag = texttag.replace(">", "&gt;");
                        texttag = texttag.replace("<", "&lt;");
                        // System.out.println(texttag);
                        if (extractFile(zip, name, texttag)) {
                            return true;
                        } else {
                            return false;
                        }

                    }
                } catch (NullPointerException a) {
                    // a.printStackTrace();
                    return false;
                    // break;
                }
            }
            // System.out.println("Done");
        } catch (IOException e) {
            // TODO Auto-generated catch block

            e.printStackTrace();
            return false;
        }
    }
}

错误信息:

Exception in thread "AWT-EventQueue-0" java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Unknown Source)
    at java.lang.AbstractStringBuilder.expandCapacity(Unknown Source)
    at java.lang.AbstractStringBuilder.ensureCapacityInternal(Unknown Source)
    at java.lang.AbstractStringBuilder.append(Unknown Source)
    at java.lang.StringBuilder.append(Unknown Source)
    at DocxCompair.extractFile(DocxCompair.java:26)
    at DocxCompair.Readfile(DocxCompair.java:58)
    at Main.lambda$0(Main.java:37)
    at Main$$Lambda$17/772250001.accept(Unknown Source)
    at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(Unknown Source)
    at java.util.stream.ReferencePipeline$3$1.accept(Unknown Source)
    at java.util.Iterator.forEachRemaining(Unknown Source)
    at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Unknown Source)
    at java.util.stream.AbstractPipeline.copyInto(Unknown Source)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(Unknown Source)
    at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(Unknown Source)
    at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(Unknown Source)
    at java.util.stream.AbstractPipeline.evaluate(Unknown Source)
    at java.util.stream.ReferencePipeline.forEach(Unknown Source)
    at Main.filelistandfind(Main.java:32)
    at Main$2.mouseClicked(Main.java:106)
    at java.awt.AWTEventMulticaster.mouseClicked(Unknown Source)
    at java.awt.Component.processMouseEvent(Unknown Source)
    at javax.swing.JComponent.processMouseEvent(Unknown Source)
    at java.awt.Component.processEvent(Unknown Source)
    at java.awt.Container.processEvent(Unknown Source)
    at java.awt.Component.dispatchEventImpl(Unknown Source)
    at java.awt.Container.dispatchEventImpl(Unknown Source)
    at java.awt.Component.dispatchEvent(Unknown Source)
    at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
    at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
    at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)

为什么你还要使用 File?你可以使用 zip 文件系统提供者。 - fge
你如何执行这段代码?如果在IDE中运行,请尝试提供更多的堆空间。 - Valijon
这怎么算是“泄漏”? - m0skit0
2个回答

4
你正在构建一个巨大的字符串。看看你的代码:
        result = "";
        len = 0;
        while ((len = zipIn.read(buffer)) > 0) {
            // System.out.print(new String(buffer));
            System.gc();
            result += result + new String(buffer);
        }

假设我每次读取两个字节。以下是该代码的执行过程。

result = ab // empty + empty + new stuff
result = ababcd // result + result + new stuff.
result = ababcdababcdef // result + result + new stuff
result = ababcdababcdefababcdababcdefgh // result + result + new stuff

每次读取200k数据块时,您都会将结果的大小翻倍,然后再添加200k。这意味着对于一个2mb文件(10个200k的读取),您的结果实际上超过204mb。
原因是因为您正在使用“result += result + new String(buffer);”,而应该使用“result += new String(buffer)”。
或者更好的方法是使用StringBuffer并在读取时进行追加。
StringBuffer result = new StringBuffer();
len = 0;
while ((len = zipIn.read(buffer)) > 0 ) {
    result.append( new String(buffer));
}

是的...对于一个大小为N的文件,操作者正在使用O(N^2)的空间。并且分配了O(N^3)的空间! - Stephen C

0

首先,你没有正确关闭所有资源。

其次,有几件事情你可以尝试。

首先,有一个叫做Files.find()的方法可以使用。你可以试试这个:

private static final BiPredicate<Path, BasicFileAttributes> DOCX_FILES
    = (path, attrs) -> attrs.isRegularFile 
    && path.getFileName().toString().endsWith(".docx");

final Path basePath = Paths.get("C:\\Users\\localness\\Desktop\\Locker");
try (
    final Stream<Path> stream = Files.find(basePath, Integer.MAX_VALUE, DOCX_FILES);
) {
    stream.forEach(path -> whatever)
}

请注意,我在这里关闭了流。
现在,看起来你想要做的是将docx文件作为zip文件打开,并读取名为word/document.xml的条目的内容。同样,在此处使用JSR 203:
final URI uri = URI.create("jar:" + docxPath.toUri());

try (
    final FileSystem zipfs = FileSystems.newFileSystem(uri, Collections.emptyMap());
    final Reader reader = Files.newBufferedReader(zipfs.getPath("word/document.xml"),
        StandardCharsets.UTF_8);
) {
    // use the reader
} catch (FileNotFoundException ignored) {
    // no such file in zip
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接