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