Java 如何在无限循环中使用更少的堆空间?
我的java应用程序的内存使用有问题。同时具有堆空间和非堆空间。现在,我专注于堆空间 我的应用程序是一个SocketServer,它通过DataInputStream获取输入。我以字节数组的形式读取信息。我每秒有不规则的输入量,但我们说的是每秒400字节到1000字节的空间,峰值可能会更高 由于我的程序是一个服务器,它在无休止的循环中等待输入。现在我遇到了一个问题,我的堆空间随着时间的推移不断增加,在5-10分钟内,它会增加0,5MB 我使用了多种监视器应用程序,比如jconsole和YourProfiler。之后,我试图借助堆转储来解决这个问题,我使用jmap对堆转储进行处理,并使用Eclipse内存分析器进行分析 现在我的问题是,在这个示例代码中,哪个选项更好,或者说使用更少的堆空间1或2 选项1:Java 如何在无限循环中使用更少的堆空间?,java,loops,memory-leaks,heap-memory,heap-dump,Java,Loops,Memory Leaks,Heap Memory,Heap Dump,我的java应用程序的内存使用有问题。同时具有堆空间和非堆空间。现在,我专注于堆空间 我的应用程序是一个SocketServer,它通过DataInputStream获取输入。我以字节数组的形式读取信息。我每秒有不规则的输入量,但我们说的是每秒400字节到1000字节的空间,峰值可能会更高 由于我的程序是一个服务器,它在无休止的循环中等待输入。现在我遇到了一个问题,我的堆空间随着时间的推移不断增加,在5-10分钟内,它会增加0,5MB 我使用了多种监视器应用程序,比如jconsole和YourP
while (true){
byte [] one= new byte [21];
do something with one;
byte [] two= new byte [50];
do something with two;
byte [] three= new byte [30];
do something with three;
}
byte [] one;
byte [] two;
byte [] three;
while (true){
one= new byte [21];
do something with one;
two= new byte [50];
do something with two;
three= new byte [30];
do something with three;
}
选项2:
while (true){
byte [] one= new byte [21];
do something with one;
byte [] two= new byte [50];
do something with two;
byte [] three= new byte [30];
do something with three;
}
byte [] one;
byte [] two;
byte [] three;
while (true){
one= new byte [21];
do something with one;
two= new byte [50];
do something with two;
three= new byte [30];
do something with three;
}
我不知道在循环中创建的三个对象会发生什么。这些应该是局部变量,并且仅在循环中可见和可访问。但是在一个循环循环之后,JVM将删除它们并在下一个循环中创建新的循环。我想应该没有内存泄漏吧
在第二个选项中,三个变量在循环外声明,因此它们在整个时间内都是活动的。在循环中,这些对象的引用会发生变化,因此不会有对旧内容的引用,这意味着它会被删除,分别由GC收集
在这两个选项中,每秒大约有4个圆圈
提前感谢您的帮助
JUnit测试结果:
while (true){
byte [] one= new byte [21];
do something with one;
byte [] two= new byte [50];
do something with two;
byte [] three= new byte [30];
do something with three;
}
byte [] one;
byte [] two;
byte [] three;
while (true){
one= new byte [21];
do something with one;
two= new byte [50];
do something with two;
three= new byte [30];
do something with three;
}
变量
one
、two
和three
只是引用:它们本身并不保存值,而是引用堆中存储实际数组对象的位置
因此,就分配的对象数量而言,这两种方法没有区别
选项3:在循环外部分配阵列,并重用相同的阵列:
byte [] one= new byte [21];
byte [] two= new byte [50];
byte [] three= new byte [30];
while (true){
// If necessary, zero out the arrays so that data from the previous
// iteration is not used accidentally.
Arrays.fill(one, (byte) 0);
Arrays.fill(two, (byte) 0);
Arrays.fill(three, (byte) 0);
// Rest of the loop.
}
这会预先分配数组,因此只创建3个数组对象,而不是(3*#迭代)
数组对象
请注意,只有在不泄漏对数组的引用的情况下,才能使用这种方法,例如,将数组放入循环体外部的列表中
要证明OP的两种方法中的内存分配是相同的,请尝试反编译代码:
public static void inLoop() {
while (true) {
byte[] one = new byte[21];
byte[] two = new byte[50];
byte[] three = new byte[30];
}
}
public static void outsideLoop() {
byte[] one;
byte[] two;
byte[] three;
while (true) {
one = new byte[21];
two = new byte[50];
three = new byte[30];
}
}
这两种方法反编译为相同的字节码:
public static void inLoop();
Code:
0: bipush 21
2: newarray byte
4: astore_0
5: bipush 50
7: newarray byte
9: astore_1
10: bipush 30
12: newarray byte
14: astore_2
15: goto 0
public static void outsideLoop();
Code:
0: bipush 21
2: newarray byte
4: astore_0
5: bipush 50
7: newarray byte
9: astore_1
10: bipush 30
12: newarray byte
14: astore_2
15: goto 0
因此,运行时内存分配必须相同。变量
one
、two
和three
只是引用:它们本身并不保存值,而是引用堆中存储实际数组对象的位置
因此,就分配的对象数量而言,这两种方法没有区别
选项3:在循环外部分配阵列,并重用相同的阵列:
byte [] one= new byte [21];
byte [] two= new byte [50];
byte [] three= new byte [30];
while (true){
// If necessary, zero out the arrays so that data from the previous
// iteration is not used accidentally.
Arrays.fill(one, (byte) 0);
Arrays.fill(two, (byte) 0);
Arrays.fill(three, (byte) 0);
// Rest of the loop.
}
这会预先分配数组,因此只创建3个数组对象,而不是(3*#迭代)
数组对象
请注意,只有在不泄漏对数组的引用的情况下,才能使用这种方法,例如,将数组放入循环体外部的列表中
要证明OP的两种方法中的内存分配是相同的,请尝试反编译代码:
public static void inLoop() {
while (true) {
byte[] one = new byte[21];
byte[] two = new byte[50];
byte[] three = new byte[30];
}
}
public static void outsideLoop() {
byte[] one;
byte[] two;
byte[] three;
while (true) {
one = new byte[21];
two = new byte[50];
three = new byte[30];
}
}
这两种方法反编译为相同的字节码:
public static void inLoop();
Code:
0: bipush 21
2: newarray byte
4: astore_0
5: bipush 50
7: newarray byte
9: astore_1
10: bipush 30
12: newarray byte
14: astore_2
15: goto 0
public static void outsideLoop();
Code:
0: bipush 21
2: newarray byte
4: astore_0
5: bipush 50
7: newarray byte
9: astore_1
10: bipush 30
12: newarray byte
14: astore_2
15: goto 0
这样,运行时内存分配必须是相同的。
让我们考虑<代码>选项2 < /C>:< /P>
第二次迭代考虑如下:
1=新字节[21]代码>
创建数组对象新字节[21]
但未分配给one
时,内存中将有两个对象:
在第一次迭代中创建并指定给的对象
一个
。此对象仍在范围内,因此不符合GC
刚刚创建但尚未分配给一个的对象。此对象也在范围内,不符合GC
因此,选项2
中的内存使用量将超过选项1
中的内存使用量,在类似情况下,在第一次迭代中创建的对象将超出范围,并有资格进行垃圾收集
因此,选项1
在堆空间使用方面更好
以下是证明我观点的程序:
import org.junit.Assert;
import org.junit.Test;
public class VariableDeclarationTest {
// this value may be increased or decreased as per system
private static final int ARRAY_SIZE = 5400;
@Test
public void testDeclareVariableInsideLoop() {
System.out.println("\n--------testDeclareVariableInsideLoop --------");
boolean successFlag = false;
for (int i = 1; i <= 3; i++) {
System.out.println("iteration: " + i);
Integer[][] arr = getLargeArray(ARRAY_SIZE); // declare inside loop
System.out.println("Got an array of size: " + arr.length);
successFlag = true;
}
Assert.assertEquals(true, successFlag);
}
@Test(expected = OutOfMemoryError.class)
public void testDeclareVariableOutsideLoop() {
System.out.println("\n---------testDeclareVariableOutsideLoop --------");
Integer[][] arr = null; // declare outside loop
for (int i = 1; i <= 3; i++) {
System.out.println("iteration: " + i);
arr = getLargeArray(ARRAY_SIZE);
System.out.println("Got an array of size: " + arr.length);
}
}
private Integer[][] getLargeArray(int size) {
System.out.print("starts producing array....");
Integer[][] arr = new Integer[size][size];
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[i].length; j++) {
arr[i][j] = size;
}
}
System.out.println(" completed");
return arr;
}
}
import org.junit.Assert;
导入org.junit.Test;
公共类VariableDeclarationTest{
//该值可根据系统的不同而增加或减少
私有静态最终整数数组_SIZE=5400;
@试验
public void testdeclarevariableindeloop(){
System.out.println(“\n-------testdeclarevariableindeloop------”;
布尔successFlag=false;
对于(int i=1;i让我们考虑<代码>选项2 < /> >:
第二次迭代考虑如下:
one=新字节[21];
创建数组对象新字节[21]
但未分配给one
时,内存中将有两个对象:
在第一次迭代中创建并指定给的对象
one
。此对象仍在范围内,因此不符合GC
刚刚创建但尚未分配给one
的对象。此对象也在范围内,不符合GC
因此,选项2
中的内存使用量将超过选项1
中的内存使用量,在类似情况下,在第一次迭代中创建的对象将超出范围,并有资格进行垃圾收集
因此,选项1
在堆空间使用方面更好
F