Java 将递归组合查找算法转换为迭代算法以避免超出GC开销限制的错误

Java 将递归组合查找算法转换为迭代算法以避免超出GC开销限制的错误,java,recursion,garbage-collection,iteration,combinations,Java,Recursion,Garbage Collection,Iteration,Combinations,该算法的目的是生成行程序列列表。每次行程都有一个起点和终点。用户同时指定这两个,算法返回的列表中的每个序列最终必须以具有指定起点的行程开始,以具有指定终点的行程结束 我提出了一个算法,返回从具有指定起点的行程开始的每个可能序列(例如,对于起点a,可能的第一次行程包括AB、AC、AD等)。在获得序列列表后,我的计划是删除所有不以具有指定终点的行程结束的序列(例如,对于C端,具有最终行程BA、CD和DB的序列将从列表中删除) 具有指定的起始点时,将返回以下序列: AB AB BA AB BC AB

该算法的目的是生成行程序列列表。每次行程都有一个起点和终点。用户同时指定这两个,算法返回的列表中的每个序列最终必须以具有指定起点的行程开始,以具有指定终点的行程结束

我提出了一个算法,返回从具有指定起点的行程开始的每个可能序列(例如,对于起点a,可能的第一次行程包括AB、AC、AD等)。在获得序列列表后,我的计划是删除所有不以具有指定终点的行程结束的序列(例如,对于C端,具有最终行程BA、CD和DB的序列将从列表中删除)

具有指定的起始点时,将返回以下序列:

AB
AB BA
AB BC
AB BC CA
AB BC CB
AB BC CB BA
当然,在这一点之后,我必须删除所有没有以正确的结束位置结束的序列,但这似乎并不太困难。当我试图将其用于一个较大的行程列表时,问题就出现了,该列表的位置比我在测试中使用的位置(即a B C D)要多。我得到这个错误:

java.lang.OutOfMemoryError: GC overhead limit exceeded
我不熟悉递归的概念,所以我没有预料到这个问题,但我现在明白了为什么会发生这种情况。我正在努力决定我应该做什么而不是这个

  • 这个算法的迭代版本会是什么样子?迭代会使用足够少的内存吗
  • 增加堆大小以避免此错误是一个好主意吗
我试图完全重写算法以解决主要问题(即生成具有指定起点和终点的序列),但我被卡住了

如果有任何帮助,我将不胜感激

编辑 下面是算法中使用的类

public class Location {

    private String continent;
    private String country;

    public Location(){
        super();
    }

    public Location(String continent, String country) {
        super();
        this.continent = continent;
        this.country = country;
    }

    public String getContinent() {
        return continent;
    }

    public void setContinent(String continent) {
        this.continent = continent;
    }

    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }   
}

public class Trip {

    private Location start;
    private Location end;

    public Location getStart() {
        return start;
    }

    public void setStart(Location start) {
        this.start = start;
    }

    public Location getEnd() {
        return end;
    }

    public void setEnd(Location end) {
        this.end = end;
    }
}
编辑 以下生成器创建的列表的大小与导致我的错误的列表的大小相同

public class Tester {

    public Trip makeTrip(Location start, Location end) {
        Trip trip = new Trip();
        trip.setStart(start);
        trip.setEnd(end);
        return trip;
    }

    public static void main(String[] args) {

    Tester tester = new Tester();

    ArrayList<Location> locations = new ArrayList<Location>();

    locations.add(new Location("A", "A"));
    locations.add(new Location("B", "B"));
    locations.add(new Location("C", "C"));
    locations.add(new Location("D", "D"));
    locations.add(new Location("E", "E"));
    locations.add(new Location("F", "F"));
    locations.add(new Location("G", "G"));
    locations.add(new Location("H", "H"));
    locations.add(new Location("I", "I"));
    locations.add(new Location("J", "J"));
    locations.add(new Location("K", "K"));
    locations.add(new Location("L", "L"));
    locations.add(new Location("M", "M"));
    locations.add(new Location("N", "N"));
    locations.add(new Location("O", "O"));
    locations.add(new Location("P", "P"));
    locations.add(new Location("Q", "Q"));
    locations.add(new Location("R", "R"));
    locations.add(new Location("S", "S"));
    locations.add(new Location("T", "T"));

    int i = 0;

    Random random = new Random();

    ArrayList<Trip> trips = new ArrayList<Trip>();

    while (i++ < 54) {
        trips.add(tester.makeTrip(locations.get(random.nextInt(20)), locations.get(random.nextInt(20))));
    }

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

    for (Trip trip : trips)

    System.out.println(trip.getStart().getCountry() + trip.getEnd().getCountry());
    }
}
公共类测试器{
公共出行行程(地点开始、地点结束){
跳闸=新跳闸();
跳闸设置启动(启动);
trip.setEnd(结束);
回程;
}
公共静态void main(字符串[]args){
测试仪=新测试仪();
ArrayList位置=新的ArrayList();
地点。添加(新地点(“A”、“A”);
地点。添加(新地点(“B”、“B”);
地点。添加(新地点(“C”、“C”);
地点。添加(新地点(“D”、“D”);
地点。添加(新地点(“E”、“E”);
地点。添加(新地点(“F”、“F”);
地点。添加(新地点(“G”、“G”);
位置。添加(新位置(“H”、“H”));
地点。添加(新地点(“I”、“I”));
地点。增加(新地点(“J”、“J”));
位置。添加(新位置(“K”、“K”));
地点。添加(新地点(“L”、“L”);
位置。添加(新位置(“M”、“M”));
位置。添加(新位置(“N”、“N”));
位置。添加(新位置(“O”、“O”));
地点。增加(新地点(“P”、“P”));
位置。添加(新位置(“Q”、“Q”));
位置。添加(新位置(“R”、“R”));
地点。添加(新地点(“S”、“S”);
位置。添加(新位置(“T”、“T”));
int i=0;
随机=新随机();
ArrayList trips=新建ArrayList();
而(i++<54){
trips.add(tester.makeTrip(locations.get(random.nextInt(20)),locations.get(random.nextInt(20));
}
System.out.println(trips.size());
用于(行程:行程)
System.out.println(trip.getStart().getCountry()+trip.getEnd().getCountry());
}
}

听起来像是一个典型的内存问题

  • 检查代码:在
    for
    循环中调用
    getTripSequences
    函数递归。这根本不是最优的
  • 将内存大小增加到
    -Xmx512m
    甚至
    -Xmx1024m

  • 使用库的数学组合执行相同的算法(组合库)

    下面是代码,它使用的内存很少,但仍然需要很长时间,因为使用暴力,尝试所有可能的组合,并过滤符合规则的组合

    您需要Java8来编译此文件,请检查是否适合您的需要


    java.lang.OutOfMemoryError:超出GC开销限制不是“真正的”内存不足错误。这意味着GC正在尝试运行,但是它花了太长的时间来确定它可以清理什么,所以它会退出。我以前遇到过这个问题,在各种复杂的情况下,GC很难弄清楚它可以释放什么,这是一个边界错误。解决方案是使用JVM参数
    -XX:-usegcOverdeLimit
    。由于您正在执行一些非常昂贵的操作,您可能仍然会遇到内存错误,但它可能会找到一些可用空间,让您继续


    另外,将这两个变量定义
    intermSq
    smallerList
    从for循环的中间拉出,作为一个小优化。如果可能大的话,你也可以考虑把它们初始化到正确的大小,否则你会有很多内存改组。您可以将一个小项目与此算法中使用的类放在一起,我不能确切地理解您正在执行的递归,但看起来它需要重写,因为它不太复杂,您需要为它增加堆大小。@KennedyOliveira我添加了算法使用的类。我同意不需要我增加堆的大小。我一直在思考如何用另一种方法来做这件事,但我没有取得多大进展。你能为测试添加一些输入吗?我是说,一些导致记忆丧失的东西?因此,我可以模拟、分析和检查间隙以提供帮助you@KennedyOliveira我在帖子中添加了一个类,该类将生成一个与导致内存错误的列表大小相同的列表。检查我是否正确,我发布了重构算法的结果,但我不确定是否更改了逻辑,请检查结果:如果
    public class Location {
    
        private String continent;
        private String country;
    
        public Location(){
            super();
        }
    
        public Location(String continent, String country) {
            super();
            this.continent = continent;
            this.country = country;
        }
    
        public String getContinent() {
            return continent;
        }
    
        public void setContinent(String continent) {
            this.continent = continent;
        }
    
        public String getCountry() {
            return country;
        }
    
        public void setCountry(String country) {
            this.country = country;
        }   
    }
    
    public class Trip {
    
        private Location start;
        private Location end;
    
        public Location getStart() {
            return start;
        }
    
        public void setStart(Location start) {
            this.start = start;
        }
    
        public Location getEnd() {
            return end;
        }
    
        public void setEnd(Location end) {
            this.end = end;
        }
    }
    
    public class Tester {
    
        public Trip makeTrip(Location start, Location end) {
            Trip trip = new Trip();
            trip.setStart(start);
            trip.setEnd(end);
            return trip;
        }
    
        public static void main(String[] args) {
    
        Tester tester = new Tester();
    
        ArrayList<Location> locations = new ArrayList<Location>();
    
        locations.add(new Location("A", "A"));
        locations.add(new Location("B", "B"));
        locations.add(new Location("C", "C"));
        locations.add(new Location("D", "D"));
        locations.add(new Location("E", "E"));
        locations.add(new Location("F", "F"));
        locations.add(new Location("G", "G"));
        locations.add(new Location("H", "H"));
        locations.add(new Location("I", "I"));
        locations.add(new Location("J", "J"));
        locations.add(new Location("K", "K"));
        locations.add(new Location("L", "L"));
        locations.add(new Location("M", "M"));
        locations.add(new Location("N", "N"));
        locations.add(new Location("O", "O"));
        locations.add(new Location("P", "P"));
        locations.add(new Location("Q", "Q"));
        locations.add(new Location("R", "R"));
        locations.add(new Location("S", "S"));
        locations.add(new Location("T", "T"));
    
        int i = 0;
    
        Random random = new Random();
    
        ArrayList<Trip> trips = new ArrayList<Trip>();
    
        while (i++ < 54) {
            trips.add(tester.makeTrip(locations.get(random.nextInt(20)), locations.get(random.nextInt(20))));
        }
    
        System.out.println(trips.size());
    
        for (Trip trip : trips)
    
        System.out.println(trip.getStart().getCountry() + trip.getEnd().getCountry());
        }
    }