Java 数组创建导致超出了垃圾收集器开销限制
你好, 我的程序花了很多时间在堆内存中创建对象,因此在某些时候我会出现以下错误: 线程“main”java.lang.OutOfMemoryError中出现异常:超出GC开销限制 我不能把我的全部应用程序放在这个讨论中,所以我创建了一个原型来解释我的程序在做什么 我的程序中处理创建对象的部分如下所示:Java 数组创建导致超出了垃圾收集器开销限制,java,arrays,garbage-collection,heap-memory,Java,Arrays,Garbage Collection,Heap Memory,你好, 我的程序花了很多时间在堆内存中创建对象,因此在某些时候我会出现以下错误: 线程“main”java.lang.OutOfMemoryError中出现异常:超出GC开销限制 我不能把我的全部应用程序放在这个讨论中,所以我创建了一个原型来解释我的程序在做什么 我的程序中处理创建对象的部分如下所示: **The Calling Program :** public class Example { public static void main(String[] args) {
**The Calling Program :**
public class Example {
public static void main(String[] args) {
ArrayList list = new ArrayList();
for (int i = 0; i < 5000; i++) {
for (int j = 0; j < 5000; j++) {
if (i==j)
continue;
int weight = new Random().nextInt();
Edge edge = new Edge(new Vertex(i+""), new Vertex(j+""),weight);
list.add(edge);
}
}
}
班级优势:
public class Vertex {
private String sequence ;
public Vertex() {
}
public Vertex(String seq) {
this.sequence = seq;
}
}
public class Edge {
private Vertex source;
private Vertex destination;
private int weight;
public Edge() {
}
public Edge(Vertex source, Vertex destination, int weight) {
int[][] array = new int[500][500];
//here i need the array to do some calculation
anotherClass.someCalculation(array,source,destination);
this.source = source;
this.destination = destination;
this.weight = weight;
}
}
如你所见:
我有5000个顶点,我需要创建5000*5000条边,每条边都有一个长度为500*500的数组
由于这个原因,在堆内存中分配的内存在特定时间结束,从我读到的许多讨论中我了解到的问题是,无法保证垃圾收集器将释放内存
那么这个问题的解决方案是什么呢?通常情况下,在构造边缘后,我不需要边缘的数组;阵列仅在构造边的过程中需要
另一个问题:如何最大限度地减少程序中的内存利用率?我试图将int数组转换为char数组,但没有效果
非常感谢您的程序实际上创建了2*5000*5000个顶点,即内部(j)循环的每次迭代都有一对顶点。我认为,在创建边时,首先需要创建5k个顶点,并在数组中保留对它们的引用 我假设“array”变量仅用于计算。如果这是真的,则使用局部变量而不是实例变量。这样,您就不必保留该数组的5k个实例——在完成计算时只保留一个实例——从而减少了内存使用 重新定义边类:
public class Edge {
private Vertex source;
private Vertex destination;
private int weight;
//private int [][] array; // don't keep an instance copy of this array if
// if it is not needed beyond the calculation.
public Edge() {
}
public Edge(Vertex source, Vertex destination, int weight) {
int[][] array = new int[500][500]; // array will gc eligible after
// constructor completes
//here i need the array to do some calculation
anotherClass.someCalculation(array,source,destination);
this.source = source;
this.destination = destination;
this.weight = weight;
}
}代码出现问题的原因如下。 每次创建Edge类的对象时
Edge edge = new Edge(new Vertex(i), new Vertex(j),weight);
调用以下构造函数
public Edge(Vertex source, Vertex destination, int weight)
因此,对于Edge
类的每个对象创建,将创建以下500 X 500整数数组
int[][] array = new int[500][500];
这会消耗大量内存,因为您要创建大约5000 X 5000个Edge
类的对象
因此,如果要存储边权重,请尝试为其创建一个全局整数数组 知道数组索引引用是4字节很重要。所以定义为500500的二维数组是1MB,没有任何数据存储。您还创建了5000 x 5000个。仅不包含其他数据的阵列将超过25TB 当GC将大部分时间用于垃圾收集并查看结果时,会出现GC开销限制。这是意料之中的,因为创建的所有数组都可以从主线程访问 您应该根据需要创建和扩展阵列(想想ArrayList),或者只在需要时创建阵列
如果绝对需要内存中的所有边,则需要增加堆或学习序列化结果并仅加载计算所需的边。此数组的用途是什么?存储某些计算的结果以供进一步使用?目的是用一些计算值填充数组,最终得到一个值作为边的权重。@Holger不幸的是,这只是一个看起来非常像我的实际应用程序的示例,你的第一句话是对的,所以我删除了所有不必要的对象创建,您关于int-to-String的第二句话是一个错误。我尝试的解决方案是使用更多堆内存,我的应用程序花费了很多时间,所以我在等待结果。好的,我现在假设您确实需要
int
值表示为String
s inVertex
对象。然后,您仍然在为5000个不同的int
值创建近50000000个不同的String
实例(在两个嵌套循环中)。最容易修复的是表示i
的字符串,因为i
在内部循环期间不会改变,因此在进入内部循环之前在外部循环中创建字符串将使字符串数量减半。但是,更好的解决方案是在开始时创建一个数组,包含所有5000个字符串,并且只使用这些字符串。@Holger我很抱歉被误解了,我使用int或String只是为了便于示例,通常Vertex类中的字符串序列是一个具有300-500个字符的序列。正如我所解释的,问题是我正在创建大量对象的两个For循环,Java垃圾收集器没有释放内存。首先,对于每个边,我有一个大小介于300到500之间的整数数组,数组的大小是不固定的,只有在示例中,我才将其设置为500。其次,我使用多线程,不能只使用一个数组。@zakzak说真的,你写的东西需要25 TB的内存。要么得到它们,要么重写它。如果阵列仅用于构建边缘
,则不要将其放入。您可以使用包含数组的EdgeBuilder
,并在不使用数组的情况下构建Edge
。您可以使用ThreadLocal
为每个线程提供一个数组。有无数的可能性(但是去建筑商那里)。请你再解释一点,我不明白你的想法。谢谢你的想法,它通过创造更多的优势帮助了一点,不幸的是,在某些时间,甚至比前一个时间更长,它会产生内存错误。原始代码中的一个关键问题是,您正在为创建的每条边创建两个新顶点。我很确定你实际上只想要5000个顶点,而不是2*5000*5000=50000000。您应该首先创建5000个顶点,并将它们保持在一个数组中。然后,在循环中的循环中,您应该引用数组来分配顶点对,而不是为前两个参数调用new(在Edge构造函数调用中)