避免OutOfMemoryError

时间:2015-02-25 21:08:26

标签: java out-of-memory permutation

我最近在Java中创建了一个方法来获取字符串的排列,但是当字符串太长时它会抛出它:java.lang.OutOfMemoryError:Java堆空间 我确信该方法是有效的,因此我需要有关如何分散计算以避免错误的建议。 我在Eclipse中使用控制台运行它。

public static ArrayList<String> permutation(String s) {
        ArrayList<String> res = new ArrayList<String>();
        if (s.length() == 1) {
            res.add(s);
        } else if (s.length() > 1) {
            int lastIndex = s.length() - 1;
            String last = s.substring(lastIndex);
            String rest = s.substring(0, lastIndex);
            res = merge(permutation(rest), last);
        }
        return res;
    }
    public static int factorial(int n) {
        int fact = 1;
        for (int i = 1; i <= n; i++) {
            fact *= i;
        }
        return fact;
    }
    public static ArrayList<String> merge(ArrayList<String> list, String c) {
        ArrayList<String> res = new ArrayList<String>();
        for (String s : list) {
            for (int i = 0; i <= s.length(); ++i) {
                String ps = new StringBuffer(s).insert(i, c).toString();
                res.add(ps);
            }
        }
        return res;
    }

电贺, 罗宾

4 个答案:

答案 0 :(得分:1)

您可以增加堆空间,如下所示:

http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html

使用

java -Xms<initial heap size> -Xmx<maximum heap size> 
命令行上的

。默认情况下,值为32米和128米。

答案 1 :(得分:1)

首先,您需要确保了解导致OutOfMemoryError(OOME)的原因。你做了几个参考,随着时间推移计算​​。我可能会误解你的意思,但如上所述,这种情况不会有任何不同。

OOME是1的结果.JVM没有足够大的空闲连续空间块来存储新对象 释放所有垃圾并压缩堆之后。在这种情况下,时间并不是真正的因素。如果内存可以访问,则无论它存在多长时间都无法收集。

因此,您可能会遇到一些问题。一个是你正在尝试创建一个非常大的对象(字符串允许你这样做)并且没有足够大的空闲块来容纳它。另一个原因是你已经用不可收集的对象填满了堆,因为你仍在引用它们。

在这种情况下,我认为问题在于您将所有字符串放入arraylist中,因此在方法执行结束之前无法收集它们。值得注意的是,substr实际上创建了一个引用原始字符串的字符数组的新字符串,因此不允许它被收集。如果您想从大字符串中提取一小段文本并丢弃其余文本,则可能会出现问题。我不认为这是一个问题,但很高兴知道所有相同。

减少内存的最有效方法是创建自己的Collection类,并在调用迭代器时生成每个排列。这将消除同时将所有排列存储在存储器中的需要。如果我得到一分钟,我会发布一些示例代码。

编辑:我在不改变算法基本结构的情况下编写了一个示例。它应该在普通的JVM中运行,以获得比您想要等待完成的更大的输入。基本上,通过将所有结果放入Arraylist,您的内存消耗相对于输入字符串长度增加了一个因子速率。通过消除结果的存储,下面的方法具有相对于输入字符串长度线性增长的内存使用。

这并不完美,我要留给读者解决一些问题。提示:如果使用空字符串创建Permutor会发生什么?

正如有人提到的,StringBuffer不是最好的选择,但你可能会更好地使用substring并连接类似的东西:

current.substring(0, position).concat(last).concat(current.substring(position));

示例:

public class Example
{
    public static void main(String... args) {
        Permutor p = new Permutor("abcdefghijklmnopqrstuvwxyz");

        System.out.println(p.size());

        int i = 0;

        for (String s : p) {
          System.out.println(i++ + ": " + s);
        }
    }


    public static int factorial(int n) {
        int fact = 1;
        for (int i = 1; i <= n; i++) {
            fact *= i;
        }
        return fact;
    }

    public static class Permutor extends AbstractCollection<String>
    {
        private String characters;

        public Permutor(String s)
        {
            characters = s;
        }

        @Override
        public Iterator<String> iterator()
        {
            if (characters.length() == 1) {
                return Collections.singleton(characters).iterator();
            } else {
                return new PermutingIterator(characters);
            }
        }

        @Override
        public int size()
        {
            return factorial(characters.length());
        }
    }

    private static class PermutingIterator implements Iterator<String>
    {
        private final char last;
        private final Iterator<String> inner;

        private String current;
        private int position;

        PermutingIterator(String s)
        {
            int lastIndex = s.length() - 1;
            this.inner = new Permutor(s.substring(0, lastIndex)).iterator();
            this.last = s.charAt(lastIndex);
        }

        @Override
        public boolean hasNext()
        {
            return inner.hasNext() || (current != null && position <= current.length());
        }

        @Override
        public String next()
        {
          while(true) {
              if (current != null && position <= current.length()) {
                  return new StringBuffer(current).insert(position++, last).toString();
              } else if (inner.hasNext()) {
                  position = 0;
                  current = inner.next();
              } else {
                  throw new IllegalStateException("no more permutations available");
              }
          }
        }

        @Override
        public void remove()
        {
          throw new UnsupportedOperationException();
        }
    }
}

答案 2 :(得分:0)

我不是算法专家或其他什么东西,但这是我想出的一个算法。 (可能已经以某种形式存在)
我也不知道它是否更有效率。

public static List<String> permutations(String s) {
    List<String> permutations = new ArrayList<String>();
    if (s.length() == 1) {
        permutations.add(s);
        return permutations;
    }
    for (int i = 0; i < s.length(); i++) {
        char starChar = s.charAt(i);
        String rest = new StringBuilder(s).deleteCharAt(i).toString();
        for (String string : permutations(rest)) {
            permutations.add(starChar + string);
        }
    }
    return permutations;
}

到目前为止,它已经能够排列长度为10的字符串 当我写这篇文章的时候,我的计算机仍然试图将长度为11的字符串置换为10分钟,而且仍然没有OutOfMemoryError。

我还注意到您在代码中使用了StringBufferStringBuffer的方法是同步的,因此更慢。 您应该使用StringBuilder代替。从方法同步的公寓中,各类几乎完全相同。

编辑:
尝试置换长度为11的字符串时出现OutOfMemoryError 所以我猜最大值是10(至少在我的机器上)。

编辑2:
您可以做的另一件事是保存硬盘驱动器上的一些计算进度。但那将是一些严谨的工作!

答案 3 :(得分:0)

您可以做的是每次通话只返回一个排列,而不是整个List。这样你就可以继续调用该方法来根据需要获得下一个排列。

class Permutation {

    private String str;
    private int first;
    private int swap;
    private BigInteger count;
    private BigInteger numPermutations;

    public Permutation(String str) {
        this.str = str;
        this.first = 0;
        this.swap = 1;
        this.count = BigInteger.ZERO;
        this.numPermutations = factorial(str.length());
    }

    public String next() {
        if (swap >= str.length()) {
            swap = 1;
            first = 0;
        }

        char[] array = str.toCharArray();
        char tmp = array[first];
        array[first] = array[swap];
        array[swap] = tmp;

        swap++;
        first++;
        count = count.add(BigInteger.ONE);

        str = String.valueOf(array);
        return str;
    }

    public boolean hasNext() {
        return count.compareTo(numPermutations) < 0;
    }

    private static BigInteger factorial(int n) {
        BigInteger fact = BigInteger.ONE;
        for (int i = 1; i <= n; i++) {
            fact = fact.multiply(BigInteger.valueOf(i));
        }
        return fact;
    }
}

这是一个使用它的例子。

Permutation perm = new Permutation("abcde");
while (perm.hasNext()) {
    System.out.println(perm.next());
}