Java OutOfMemoryError 이상한 동작
최대 메모리가 256M이라고 가정하면이 코드가 작동하는 이유는 무엇입니까?
public static void main(String... args) {
for (int i = 0; i < 2; i++)
{
byte[] a1 = new byte[150000000];
}
byte[] a2 = new byte[150000000];
}
하지만 이건 OOME을 던져?
public static void main(String... args) {
//for (int i = 0; i < 2; i++)
{
byte[] a1 = new byte[150000000];
}
byte[] a2 = new byte[150000000];
}
관점을 유지하려면이 코드를 -Xmx64m
다음 과 같이 실행하는 것이 좋습니다 .
static long sum;
public static void main(String[] args) {
System.out.println("Warming up...");
for (int i = 0; i < 100_000; i++) test(1);
System.out.println("Main call");
test(5_500_000);
System.out.println("Sum: " + sum);
}
static void test(int size) {
// for (int i = 0; i < 1; i++)
{
long[] a2 = new long[size];
sum += a2.length;
}
long[] a1 = new long[size];
sum += a1.length;
}
워밍업을하는지 건너 뛰는 지에 따라 불이 나지 않을 것입니다. 이는 JITted 코드 null
가 var를 올바르게 처리 하는 반면 해석 된 코드는 그렇지 않기 때문입니다. 두 가지 동작 모두 Java 언어 사양에 따라 허용되며 이는 JVM의 자비에 있음을 의미합니다.
Java HotSpot(TM) 64-Bit Server VM (build 23.3-b01, mixed mode)
OS X에서 테스트되었습니다 .
바이트 코드 분석
for
루프 ( sum
변수가 없는 간단한 코드)가 있는 바이트 코드를 살펴보십시오 .
static void test(int);
Code:
0: iconst_0
1: istore_1
2: goto 12
5: iload_0
6: newarray long
8: astore_2
9: iinc 1, 1
12: iload_1
13: iconst_1
14: if_icmplt 5
17: iload_0
18: newarray long
20: astore_1
21: return
그리고없이 :
static void test(int);
Code:
0: iload_0
1: newarray long
3: astore_1
4: iload_0
5: newarray long
7: astore_1
8: return
No explicit null
ing out in either case, but note that in the no-for example the same memory location is actually reused, in contrast with the for example. This would, if anything, lead to the expectation opposite to the observed behavior.
A twist...
Based on what we learned from the bytecode, try running this:
public static void main(String[] args) {
{
long[] a1 = new long[5_000_000];
}
long[] a2 = new long[0];
long[] a3 = new long[5_000_000];
}
No OOME thrown. Comment out the declaration of a2
, and it is back. We allocate more, but occupy less? Look at the bytecode:
public static void main(java.lang.String[]);
Code:
0: ldc #16 // int 5000000
2: istore_1
3: ldc #16 // int 5000000
5: newarray long
7: astore_2
8: iconst_0
9: newarray long
11: astore_2
12: ldc #16 // int 5000000
14: newarray long
16: astore_3
17: return
The location 2, used for a1
, is reused for a2
. The same is true for OP's code, but now we overwrite the location with a reference to an innocuous zero-length array, and use another location to store the reference to our huge array.
To sum it up...
Java Language Specification does not specify that any garbage object must be collected and the JVM spec only says that the "frame" with local variables is destroyed as a whole upon method completion. Therefore all behaviors that we have witnessed are by the book. The invisible state of an object (mentioned in the document linked to by keppil) is just a way to describe what happens to go on in some implementations and under some circumstances, but is in no way any kind of canonical behavior.
This is because while a1
isn't in scope after the brackets, it is in a state called invisible until the method returns.
Most modern JVMs don't set the variable a1
to null
as soon as it leaves the scope (actually, whether the inner brackets are there or not doesn't even change the generated byte code), because it is very ineffective, and usually doesn't matter. Therefore, a1
can't be garbage collected until the method returns.
You can check this by adding the line
a1 = null;
inside the brackets, which makes the program run fine.
The term invisible and the explanation is taken from this old paper: http://192.9.162.55/docs/books/performance/1st_edition/html/JPAppGC.fm.html
.
ReferenceURL : https://stackoverflow.com/questions/13531004/java-outofmemoryerror-strange-behaviour
'IT Share you' 카테고리의 다른 글
Netbeans 7.4에서 Derby 데이터베이스를 시작할 수 없습니다. (0) | 2021.01.09 |
---|---|
시작시 배치 파일 실행 (0) | 2021.01.09 |
Spring에서 ModelAndView 대 Model을 언제 사용합니까? (0) | 2021.01.08 |
TortoiseSVN 정리는 실제로 무엇을합니까? (0) | 2021.01.08 |
@ RolesAllowed-Annotation의 값 매개 변수로 Enum 형식 사용 (0) | 2021.01.08 |