2014-12-29 4 views
0

좋아, 그래서 자바에서이 작은 실험을하려고합니다. 정수로 큐를 채우고 소요 시간을 확인하고 싶습니다.Java가 예상보다 많은 메모리를 사용합니다.

import java.io.*; 
import java.util.*; 

class javaQueueTest { 
public static void main(String args[]){ 
    System.out.println("Hello World!"); 
    long startTime = System.currentTimeMillis(); 
    int i; 
    int N = 50000000; 

    ArrayDeque<Integer> Q = new ArrayDeque<Integer>(N); 
    for (i = 0;i < N; i = i+1){ 
     Q.add(i); 
    } 
    long endTime = System.currentTimeMillis(); 
    long totalTime = endTime - startTime; 
    System.out.println(totalTime); 
} 
} 

OK, 그래서 나는 이것을 실행하고 5 개 천만 정수 나쁜 아닌

Hello World! 
12396 

약 12 ​​초를 얻을 : 여기에 간다. 하지만 7 천만 개의 정수를 실행하려고하면 다음과 같이 나타납니다.

Hello World! 
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space 
    at java.lang.Integer.valueOf(Integer.java:642) 
    at javaQueueTest.main(javaQueueTest.java:14) 

이 메시지가 표시되는 데 약 10 분이 걸립니다. 흠. 그래서 만약 내가 힙에 대한 나의 모든 기억 (8gigs)을 주면 어떨까? 그래서 7GB의 힙 크기로 실행하지만 여전히 동일한 오류가 발생합니다.

javac javaQueueTest.java 
java -cp . javaQueueTest -Xmx7g 
Hello World! 
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space 
    at java.lang.Integer.valueOf(Integer.java:642) 
    at javaQueueTest.main(javaQueueTest.java:14) 

두 가지를 묻고 싶습니다. 첫째, 오류가 발생하는 데 너무 오래 걸립니까? 둘째, 이 메모리가 충분하지 않은 이유는 무엇입니까?? C에서 3 억 개의 정수를 동일한 실험으로 실행하면 (glib g_queue와 함께) 컴퓨터가 많이 느려지 긴하지만 10 초 이내에 실행할 수 있습니다. 따라서 정수의 수가 잘못되지 않아야합니다. 내가 컴파일하고 결과를 얻을

#include<stdlib.h> 
#include<stdio.h> 
#include<math.h> 
#include<glib.h> 
#include<time.h> 

int main(){ 
clock_t begin,end; 
double time_spent; 
GQueue *Q; 

begin = clock(); 
Q = g_queue_new(); 
g_queue_init(Q); 
int N = 300000000; 
int i; 
for (i = 0; i < N; i = i+1){ 
    g_queue_push_tail(Q,GINT_TO_POINTER(i)); 
} 
end = clock(); 
time_spent = (double)(end - begin)/CLOCKS_PER_SEC; 
printf("elapsed time: %f \n",time_spent); 

} 

:

try{ 
 
    ArrayDeque<Integer> Q = new ArrayDeque<Integer>(N); 
 
    for (i = 0;i < N; i = i+1){ 
 
     Q.add(i); 
 
    } 
 
} 
 
catch(OutOfMemoryError e){ 
 
    Q=null; 
 
    System.gc(); 
 
    System.err.println("OutOfMemoryError: "+i); 
 
}

: 당신이 OutOfMemoryError를 잡을 수

gcc cQueueTest.c `pkg-config --cflags --libs glib-2.0 gsl ` -o cQueueTest 
~/Desktop/Software Development/Tests $ ./cQueueTest 
elapsed time: 13.340000 
+2

자바 제네릭이 원시 객체가 아닌 정수 객체를 사용한다는 것을 잊지 마십시오. 자바가 각 int에 대해 메모리를 할당해야하기 때문에 속도가 훨씬 느립니다. 또한 기본적으로 Java VM 메모리는 64M이므로 많은 요소를 처리 할 수 ​​있다고 생각하지 않습니다. – gimpycpu

+0

의견 보내 주셔서 감사합니다. 그러나, 나는 실행 명령에 더 많은 메모리를 할당한다 : java -cp. javaQueueTest -Xmx7g, 7gigs로 지정합니다. 그래서 나는 아직도 메모리가 부족한 이유와 에러 메시지를주기 위해 너무 오래 걸리는 이유 (약 10m)를 이해하지 못합니다. 느린 성능, 나는 이해할 수있다. – burnedWood

답변

0

내 거친 생각 :

First, why does it take so long to come up with the error?

자신의 의견에 gimpycpu가 언급 한 바와 같이, 자바 당신의 RAM의 전체 메모리 인수로 시작하지 않습니다. 원하는 경우 (더 많은 양의 RAM을 사용하기 위해 64 비트 VM이 있음) VM 시작 시간에 -Xmx8g 및 -Xms8g 옵션을 추가하여 VM이 8GB의 RAM을 갖도록하고 -Xms가이를 의미합니다 또한 RAM을 사용할 수 있다고 말하는 대신 사용을 위해 RAM을 준비합니다. 이렇게하면 런타임이 크게 줄어 듭니다. 또한 이미 언급했듯이 자바 정수 복싱은 꽤 오버 헤드입니다. JVM이 단지 4 바이트 일반 정수에 의한 권투을 비교 한에서의 ArrayDeque datastructur에서 정수 참조를 사용하기 때문에 자바는 모든에 대해 소개

Why is all this memory not enough?

는 메모리 오버 헤드 약간의 객체. 따라서 모든 정수에 대해 약 20 바이트를 계산해야합니다.
당신은의 ArrayDeque 대신 INT []를 사용하려고 할 수 있습니다

import java.io.*; 
import java.util.*; 

class javaQueueTest { 
    public static void main(args){ 
     System.out.println("Hello World!"); 
     long startTime = System.currentTimeMillis(); 
     int i; 
     int N = 50000000; 
     int[] a = new int[N]; 
     for (i = 0;i < N; i = i+1){ 
      a[i] = 0; 
     } 
     long endTime = System.currentTimeMillis(); 
     long totalTime = endTime - startTime; 
     System.out.println(totalTime); 
    } 
} 

이 빠르고 일반 배열의 사용으로 인해 매우 될 것입니다. 내 시스템에서 매 실행마다 1 초 미만입니다!

+0

예 감사합니다! 나는 두 가지 방아쇠를 놓아야한다는 것을 몰랐고, 그것은 내 문제를 해결했습니다! – burnedWood

0

을 기록을 위해, 여기에 C 코드 OutOfMemoryError가 발생했을 때 보여주기 위해.

그리고 귀하의 코드를 실행 : JVM

이전 했나요으로

에 대한 힙 크기를 증가시키기 위해

java -Xmx4G javaQueueTest 

는, 자바는 ... 훨씬 느린 원시 형과 C 이상의 객체와

+0

'system.gc '를 호출하면 아무 것도 달성되지 않습니다. 그리고 일반적으로 OOME을 잡는 것은 나쁜 생각입니다. 왜냐하면 애플리케이션이 파손 된 상태에 놓이기 쉽기 때문입니다. –

+0

맞습니다. System.gc()가 무언가를하도록하기 위해 Q = null을 추가해야합니다. 어쨌든 여기서는별로 유용하지 않습니다. 반복 횟수를 인쇄하고 JVM을 적절히 유지해야합니다. –

0
입니다

귀하의 경우 GC는 적어도 일부 오브젝트가 수명이 짧을 것으로 가정하므로 어려움을 겪습니다. 귀하의 경우 모든 객체가 오래 유지되므로이 데이터를 관리하는 데 상당한 오버 헤드가 추가됩니다. Integer 래퍼 동안

당신은

Hello World! 
[GC (Allocation Failure) 1835008K->1615280K(7034368K), 3.8370127 secs] 
5327 

int 같은 출력을 얻을 N = 150000000-Xmx7g -Xms7g -verbose:gc를 사용하는 경우는, 자바 (4 -bytes)에서 원시적이다. 이 래퍼는 헤더와 패딩에 대한 참조가 필요하며 결과는 Integer이며 참조는 값 당 20 바이트를 사용합니다.

해결책은 한 번에 많은 값을 대기열에 넣지 않는 것입니다. 공급자를 사용하여 처음부터 대기열을 만들지 않아도되므로 새로운 가치를 제공 할 수 있습니다.

그래도 7GB 힙을 사용하면 ArrayQueue를 200M 이상으로 만들 수 있습니다. 질문에 대한

+0

그런 세부 사항에 응답 해 주셔서 감사합니다! – burnedWood

0

First, why does it take so long to come up with the error?

이것은 GC "죽음의 나선형"의 고전적인 예처럼 보입니다. 기본적으로 JVM은 매번 GC를 반복적으로 수행하여 매번 공간을 적게 차지합니다. 결국 JVM은 "유용한"작업을 수행하는 것보다 GC를 실행하는 데 더 많은 시간을 소비합니다. 마침내 포기합니다. 여기에 설명 된대로

이 발생하는 경우는,이 ​​솔루션은 GC 오버 헤드 제한을 구성하는 것입니다.

(자바 8은 기본적으로 GC 오버 헤드 제한을 구성하지만 당신은 예외 메시지에서 판단하여 이전 버전의 Java를 사용하는 것 같습니다.)

Second, Why is all this memory not enough?

@Peter Lawrey 's explana .

해결 방법은 제네릭을 사용하지 않는 큐 클래스를 찾거나 구현하는 것입니다. 죄송하지만이 클래스는 표준 Deque API와 호환되지 않습니다.

+0

설명 주셔서 감사합니다! 당신과 피터의 대답은 어디에서 가장 포괄적이고, 나쁘지 않은데 아직 나는 상향하지 않을 수 있습니다! – burnedWood

관련 문제