1MB 이상의 Java 바이트 배열은 RAM의 두 배를 차지합니다.


14

윈도우 10에 아래의 코드를 실행 / 오픈 JDK 11.0.4_x64는 출력으로 생산 used: 197하고 expected usage: 200. 이는 백만 개의 요소로 구성된 200 바이트 배열이 대략적으로 사용됨을 의미합니다. 200MB RAM. 다 괜찮아.

I로부터 코드에서 바이트 배열의 할당을 변경하는 경우 new byte[1000000]new byte[1048576](1024 개 * 1024 요소이라고)는 출력으로서 생성 used: 417하고 expected usage: 200. 이런 젠장?

import java.io.IOException;
import java.util.ArrayList;

public class Mem {
    private static Runtime rt = Runtime.getRuntime();
    private static long free() { return rt.maxMemory() - rt.totalMemory() + rt.freeMemory(); }
    public static void main(String[] args) throws InterruptedException, IOException {
        int blocks = 200;
        long initiallyFree = free();
        System.out.println("initially free: " + initiallyFree / 1000000);
        ArrayList<byte[]> data = new ArrayList<>();
        for (int n = 0; n < blocks; n++) { data.add(new byte[1000000]); }
        System.gc();
        Thread.sleep(2000);
        long remainingFree = free();
        System.out.println("remaining free: " + remainingFree / 1000000);
        System.out.println("used: " + (initiallyFree - remainingFree) / 1000000);
        System.out.println("expected usage: " + blocks);
        System.in.read();
    }
}

visualvm으로 조금 더 깊이 살펴보면 첫 번째 경우 모든 것이 예상대로 보입니다.

바이트 배열은 200MB를 차지합니다

두 번째 경우에는 바이트 배열 외에도 바이트 배열과 동일한 양의 RAM을 차지하는 동일한 수의 int 배열이 표시됩니다.

int 배열은 추가 200MB를 차지합니다.

그런데이 int 배열은 참조 된 것을 나타내지 않지만 가비지 수집 할 수는 없습니다 ... (바이트 배열은 참조 된 곳에서 잘 나타납니다.)

어떤 아이디어가 여기서 일어나고 있습니까?


[블록] [], 및에서 바이트의 ArrayList <바이트 [] "의 데이터를 변경하는 시도하여 for 루프의 ArrayList의 내부에 종속 제거 데이터 [I] = 새로운 바이트 [1000000]
jalynn2

더 나은 공간적 로컬 성 int[]을 위해 큰 에뮬레이션을 사용하여 내부적으로 JVM과 관련이 byte[]있습니까?
Jacob G.

@JacobG. 내부적으로는 분명히 보이지만 가이드에 표시가없는 것 같습니다 .
Kayaman

단지 두 가지 관측치 : 1. 1024 * 1024에서 16을 빼면 예상대로 작동하는 것 같습니다. 2. jdk8의 동작은 여기서 관찰 할 수있는 것과 다릅니다.
두 번째

@second 네, 마법의 한계는 어레이가 1MB의 RAM을 차지하는지 여부입니다. 1을 빼면 메모리가 런타임 효율성을 위해 채워지고 어레이의 관리 오버 헤드가 1MB로 계산됩니다. JDK8이 다르게 동작한다는 것은 재미 있습니다!
Georg

답변:


9

이것이 설명하는 것은 G1 가비지 수집기의 기본 동작 으로 일반적으로 1MB "regions"로 기본 설정되며 Java 9에서는 JVM 기본값이되었습니다. 다른 GC를 사용하여 실행하면 다양한 숫자가 나타납니다.

영역 크기의 절반보다 큰 개체는 "풍선"으로 간주됩니다. 힙 영역 크기의 배수보다 약간 큰 개체의 경우이 사용되지 않는 공간으로 인해 힙이 조각화 될 수 있습니다.

나는 달렸고 java -Xmx300M -XX:+PrintGCDetails그것은 거대한 지역에 의해 힙이 소진되었음을 보여줍니다.

[0.202s][info   ][gc,heap        ] GC(51) Old regions: 1->1
[0.202s][info   ][gc,heap        ] GC(51) Archive regions: 2->2
[0.202s][info   ][gc,heap        ] GC(51) Humongous regions: 296->296
[0.202s][info   ][gc             ] GC(51) Pause Full (G1 Humongous Allocation) 297M->297M(300M) 1.935ms
[0.202s][info   ][gc,cpu         ] GC(51) User=0.01s Sys=0.00s Real=0.00s
...
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

우리는 1MiB byte[]가 "G1 영역 크기의 절반 미만"이 되기를 원 하므로 추가 -XX:G1HeapRegionSize=4M하면 기능적 응용이 가능합니다.

[0.161s][info   ][gc,heap        ] GC(19) Humongous regions: 0->0
[0.161s][info   ][gc,metaspace   ] GC(19) Metaspace: 320K->320K(1056768K)
[0.161s][info   ][gc             ] GC(19) Pause Full (System.gc()) 274M->204M(300M) 9.702ms
remaining free: 100
used: 209
expected usage: 200

G1에 대한 심층적 인 개요 : https://www.oracle.com/technical-resources/articles/java/g1gc.html

G1에 대한 자세한 내용 : https://docs.oracle.com/en/java/javase/13/gctuning/garbage-first-garbage-collector-tuning.html#GUID-2428DA90-B93D-48E6-B336-A849ADF1C552


나는 시리얼 GC와 8MB의 소요 긴 배열과 같은 문제가 (크기 1024-1024-2 괜찮습니다)와 G1HeapRegionSize 내 경우에 아무 짓도 안 했어요 변경
GotoFinal

확실하지 않습니다. long []
drekbour

@ GotoFinal, 위의 설명에서 설명하지 않은 문제는 관찰되지 않습니다. long[1024*1024]예상되는 1600M의 G1 사용 코드를 테스트 했습니다. -XX:G1HeapRegionSize[1M 사용 : 1887, 2M 사용 : 2097, 4M 사용 : 3358, 8M 사용 : 3358, 16M 사용 : 3363, 32M 사용 : 1682]에 따라 다릅니다. 로 -XX:+UseConcMarkSweepGC사용 : 1687과 함께 -XX:+UseZGC사용 : 2105을 함께 -XX:+UseSerialGC사용 : 1698
drekbour

gist.github.com/c0a4d0c7cfb335ea9401848a6470e816 같은 단지 코드, 어떤 GC 옵션이 인쇄됩니다 변경하지 않고 used: 417 expected usage: 400하지만 난 것을 제거하는 경우 -2가 변경됩니다 used: 470그래서 50메가바이트 주위가 사라 졌어요, 50 * 2 걷고 확실히 훨씬 적은 50 메가 바이트 단위입니다
GotoFinal

1
같은 것. 차이는 ~ 50MB이며 50 개의 "엄청난"블록이 있습니다. GC 세부 사항은 다음과 같습니다. 1024 * 1024-> [0.297s][info ][gc,heap ] GC(18) Humongous regions: 450->4501024 * 1024-2-> [0.292s][info ][gc,heap ] GC(20) Humongous regions: 400->400마지막 두 longs G1이 16 바이트를 저장하기 위해 다른 1MB 영역을 할당하도록합니다.
drekbour
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.