자바 언어·플랫폼 용어 사전
실무 사례Humongous · G1 Region · 큰 배열 할당

거대 객체

G1에서 Region 절반을 넘는 객체는 특별 취급된다. 빈 공간이 많아도 OOM이 난다.

G1에서 Region 크기의 절반을 넘는 객체는 별도로 취급된다.

G1 은 힙을 균일한 Region 으로 나눈다 (1~32MB, 힙 크기로 자동 결정) Region 의 50% 를 넘는 객체 = Humongous

  • Young 을 거치지 않고 Old 의 연속된 Region 에 바로 놓인다
  • 연속된 빈 Region 이 필요하다
  • 회수 시점이 제한적이다 (예전에는 Full GC 를 기다렸다)

그래서 생기는 역설

  • 증상 — 힙 사용률 60% 인데 OutOfMemoryError

  • 원인 — 32MB 배열을 할당하려는데 Region 이 4MB

    • 연속된 8개 Region 이 필요하다
    • 빈 공간은 많지만 흩어져 있어 연속 확보 실패
  • 전형적 발생 지점

    • 대용량 파일을 통째로 byte[] 로 읽기
    • 큰 결과셋을 List 로 전부 적재 (내부 배열이 계속 2배 확장)
    • 이미지·동영상 버퍼

대응

  • ① 객체를 쪼갠다 — 스트리밍 처리로 바꾼다 (근본 대응)
    • 파일은 InputStream 으로, 쿼리는 커서/페이징으로
  • ② Region 크기를 키운다
    • -XX:G1HeapRegionSize=16m → 8MB 미만 객체는 더 이상 Humongous 가 아니다
    • 단 Region 이 커지면 수집 단위가 커져 정지 시간이 늘 수 있다
  • ③ 컬렉션 초기 용량을 미리 지정해 중간 확장 배열을 줄인다

확인 방법

-Xlog:gc+heap=debug 로 humongous 할당 로그를 본다 [gc,heap] Humongous regions: 12->12

  • 또는 GC 로그에 (G1 Humongous Allocation) 원인이 찍힌 Young GC 가 잦은지 본다
  • 큰 객체 할당이 GC 를 유발하고 있다는 신호

배열 크기 자체의 한계

자바 배열의 최대 길이는 int 범위(약 21억) 다 그보다 커야 하면 배열로는 표현할 수 없다

  • 청크 분할 · MappedByteBuffer · 오프힙 자료구조로 간다

Region 크기는 어떻게 정해지나

G1 은 기본적으로 힙을 약 2,048개 Region 으로 나누려 한다 Region 크기는 1·2·4·8·16·32MB 중 하나로 반올림된다

힙 4GB→ 2MB→ Humongous 기준 1MB
힙 8GB→ 4MB→ 2MB
힙 32GB→ 16MB→ 8MB

즉 힙을 줄이면 Humongous 기준도 함께 낮아진다 "힙을 줄였더니 없던 문제가 생겼다" 가 여기서 나온다

회수는 개선됐지만 여전히 특별하다

초기 G1 은 Humongous 를 Full GC 때만 회수했다 이후 도달 불가한 Humongous 는 Young GC 중에도 회수하도록 개선됐다

그래도 남는 성질

  • 연속 Region 이 필요하다는 제약은 그대로다
  • 할당 자체가 Region 여러 개를 한 번에 요구해 GC 를 유발한다
  • 마지막 Region 의 남는 부분은 다른 객체가 쓰지 못한다 (내부 낭비)

면접 함정

  • "힙에 여유가 있으면 OOM은 안 난다" → 연속 공간이 필요하면 난다.
  • "Region을 키우면 항상 이득" → 정지 시간이 늘 수 있다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 실무 노하우·실제 사고 사례