【问题标题】:Java permutations causes heap space errorJava 排列导致堆空间错误
【发布时间】:2015-08-13 16:56:21
【问题描述】:

我有一个程序可以生成数字组合的排列。排列具有给定的大小(在本例中为 6)。我的问题是我得到一个 java.lang.OutOfMemoryError: Java heap space error 具有更大的数字组合。例如,如果我计算 18 个数字的排列,我会得到这个异常:

Exception in thread "AWT-EventQueue-0" java.lang.OutOfMemoryError: Java heap space
at javax.swing.text.GapContent.allocateArray(Unknown Source)
at javax.swing.text.GapVector.resize(Unknown Source)
at javax.swing.text.GapVector.shiftEnd(Unknown Source)
at javax.swing.text.GapContent.shiftEnd(Unknown Source)
at javax.swing.text.GapVector.open(Unknown Source)
at javax.swing.text.GapVector.replace(Unknown Source)
at javax.swing.text.GapContent.insertString(Unknown Source)
at javax.swing.text.AbstractDocument.handleInsertString(Unknown Source)
at javax.swing.text.AbstractDocument.insertString(Unknown Source)
at javax.swing.text.PlainDocument.insertString(Unknown Source)
at javax.swing.JTextArea.append(Unknown Source)
at test.permute(test.java:48)
at test.permute(test.java:52)
at test.permute(test.java:52)
at test.permute(test.java:52)
at test.permute(test.java:52)
at test.permute(test.java:52)
at test.permute(test.java:52)
at test.actionPerformed(test.java:74)
at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(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)

现在这似乎是由打印出 JTextArea 中的排列引起的,因为如果您查看异常的第一行,您可以在 javax.swing.text 中看到错误。

这是我使用的代码:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Arrays;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.ScrollPaneConstants;

public class test extends JFrame implements ActionListener {

    private JPanel panel;
    private JButton ok;
    private JTextArea text;
    private String str;
    private JScrollPane scroll;

    public test() { 
        panel = new JPanel();
        ok = new JButton("OK");
        ok.addActionListener(this);
        str = ("1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, ");
        text = new JTextArea(str, 16, 16);
        text.setEditable(true); 
        scroll = new JScrollPane(text);
        scroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);


        panel.add(scroll);
        panel.add(ok);
        add(panel); 

        pack();
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);
    }

    public static void main(String[] args) {
        new test();
    }

    //-----------------------------------------------------------------------------------
    public void permute(java.util.List<Integer> intList, int k, int max) {
          if(k == max) {
            text.append(Arrays.toString(intList.subList(0, max).toArray()).replaceAll( "\\[|\\]", "") + ", \n");
          } else {
            for(int i = k; i < intList.size(); i++){
              java.util.Collections.swap(intList, i, k);
              permute(intList, k+1, max);
              java.util.Collections.swap(intList, k, i);
            }
          }
    }
    //-----------------------------------------------------------------------------------

    @Override
    public void actionPerformed(ActionEvent arg0) {
        String a = text.getText();  
        text.setText("");

        String[] zahlenstring = a.split(", ");
        int[] zahlenint = new int[zahlenstring.length];

        for(int i = 0; i < zahlenstring.length ;i++) {
            zahlenint[i] = Integer.parseInt(zahlenstring[i]); }

        ArrayList<Integer> intList = new ArrayList<Integer>();
        for (int i = 0; i < zahlenint.length; i++) {
            intList.add(zahlenint[i]); }

        permute(intList, 0, 6);    
    }
}

我寻找了解决方案,但我主要找到了将 ram 添加到 java 或使用多线程的建议,我不想在这里使用它们。

现在我的问题是:我怎样才能改进这段代码,让它不再给我那个例外?这里还有什么需要改进的地方吗?

如果您还有问题,可以在 cmets 中提问。提前致谢。

【问题讨论】:

  • 提示:18 个数的排列有多少?你有多少内存?
  • 您可以要求不可能,但不要期望得到有用的答案。 18!是一个很大的数字。
  • @nneonneo 我有一个 8 GB RAM 的笔记本,但只有 4 GB RAM 的笔记本需要该程序。
  • 您的代码在text.append 处失败,因此不太可能做任何不同的事情...

标签: java algorithm exception permutation heap-memory


【解决方案1】:

当您处理大量元素时,最好提前计算它们将消耗多少内存。

您的排列将消耗 字节,假设每个组合消耗 12 个字节。因此消耗的内存总量将在 160MB 左右。此内存量仅用于将组合存储为字符串。将它们添加到 TextArea 时,至少需要双倍的内存量。

解决内存问题的最简单方法是在计算排列后立即输出排列。您可以将其写入控制台或文件。在这种情况下,您不需要任何额外的内存。

但是,如果您唯一的选择是创建 GUI 应用程序,那么您可以执行以下操作。首先,您需要意识到您至少需要 (13366080 * k) 字节来存储排列字符串(其中 k 是每个排列作为字符串的大小),13366080 * 16 字节用于存储字符串数组和一些额外的内存使应用程序顺利运行(防止 GC 暂停)。如果我的计算是正确的,它将是大约 500Mb 的堆内存。

接下来,您不应将字符串直接添加到 TextArea。相反,创建排列的ArrayList&lt;String&gt;,创建 ScrollBar,使用结果数组的大小对其进行初始化。在 ScrollBar 的位置发生变化时,使用 ScrollBar 位置的元素重新初始化 TextArea(从滚动条的位置获取少量元素并将它们作为行添加到 TextArea)。

【讨论】:

    【解决方案2】:

    您需要处理每个确定的排列,而不是构建一个使用非常大(或不切实际)内存的字符串。

    public static void permute(List<Integer> intList,  Consumer<List<Integer>> permutationConsumer) {
        permute(intList, 0, intList.size(), permutationConsumer);
    }
    
    public static void permute(List<Integer> intList, int k, int max, Consumer<List<Integer>> permutationConsumer) {
       if(k == max) {
            permutationConsumer.accept(intList.subList(0, max));
            return;
       }
       for(int i = k; i < intList.size(); i++) {
            Collections.swap(intList, i, k);
            permute(intList, k + 1, max, permutationConsumer);
            Collections.swap(intList, k, i);
       }
    }
    

    此解决方案将不再使用任何内存。

    要随时显示此结果,您可以这样做

    permute(intList, System.out::println);
    

    这不会在显示之前建立一个大字符串,所以你不会耗尽内存。

    【讨论】:

    • 我不熟悉 Lambda,但是这段代码只是将每个排列存储在列表 permutationConsumer 中,对吧?所以我必须在以后输出所有排列以显示它们,这会再次产生一个大字符串或者我在这里理解错误?
    • @OlafStein 您不必使用 lambda。这样做是允许在您收到数据时对其进行处理。这是您唯一的选择,因为您没有足够的空间来构建大字符串。顺便说一句,无论你有多少内存,字符串都不能容纳超过 20 亿个字符。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-09-15
    • 2023-04-07
    • 1970-01-01
    • 1970-01-01
    • 2011-10-10
    • 1970-01-01
    相关资源
    最近更新 更多