Java에서 객체의 메모리 소비는 얼마입니까?


216

하나의 속성을 가진 100 개의 속성과 100 개의 속성을 가진 하나의 객체가 사용하는 메모리 공간이 각각 하나의 속성을 가지고 있습니까?

객체에 얼마나 많은 메모리가 할당됩니까?
속성을 추가 할 때 얼마나 많은 공간이 사용됩니까?

답변:


180

Mindprod 는 이것이 대답하기 쉬운 질문이 아니라고 지적합니다.

JVM은 임의의 패딩 또는 오버 헤드로 내부, 빅 또는 리틀 엔디안을 만족시키는 방식으로 데이터를 자유롭게 저장할 수 있지만 프리미티브는 공식적인 크기를 가진 것처럼 작동해야합니다.
예를 들어, JVM 또는 원시 컴파일러는 a boolean[]와 같은 64 비트 길이의 청크 를 저장하기로 결정할 수 있습니다 BitSet. 프로그램이 동일한 답변을 제공하는 한 귀하에게 말할 필요는 없습니다.

  • 스택에 일부 임시 객체를 할당 할 수 있습니다.
  • 일부 변수 또는 메소드 호출을 완전히 존재하지 않고 최적화하여 상수로 대체 할 수 있습니다.
  • 메소드 또는 루프의 버전을 지정할 수 있습니다. 즉, 각각 특정 상황에 최적화 된 두 가지 버전의 메소드를 컴파일 한 다음 호출 할 버전을 먼저 결정합니다.

물론 하드웨어와 OS에는 멀티 캐시, 온칩 캐시, SRAM 캐시, DRAM 캐시, 일반 RAM 작업 세트 및 디스크의 백업 저장소가 있습니다. 모든 캐시 수준에서 데이터가 복제 될 수 있습니다. 이 모든 복잡성으로 인해 RAM 소비를 매우 대략적으로 예측할 수 있습니다.

측정 방법

당신이 사용할 수있는 Instrumentation.getObjectSize()객체가 소비 한 스토리지의 추정치를 얻는 데 .

실제 객체 레이아웃, 풋 프린트 및 참조 를 시각화 하기 위해 JOL (Java Object Layout) 도구를 사용할 수 있습니다. .

객체 헤더 및 객체 참조

최신 64 비트 JDK에서 객체에는 12 바이트 헤더가 있으며 8 바이트의 배수로 채워져 있으므로 최소 객체 크기는 16 바이트입니다. 32 비트 JVM의 경우 오버 헤드는 8 바이트이며 4 바이트의 배수로 채워집니다. ( Dmitry Spikhalskiy의 답변 , Jayen의 답변JavaWorld에서 에서)

일반적으로 참조는 32 비트 플랫폼 또는 64 비트 플랫폼에서 최대 4 바이트입니다 -Xmx32G. 32Gb ( -Xmx32G) 이상 8 바이트 ( 압축 된 객체 참조를 참조하십시오 .)

결과적으로 64 비트 JVM에는 일반적으로 30-50 % 더 많은 힙 공간이 필요합니다. ( 32 비트 또는 64 비트 JVM을 사용해야합니까? , 2012, JDK 1.7)

박스 타입, 배열 및 문자열

박스형 래퍼는 기본 유형 ( JavaWorld )에 비해 오버 헤드가 있습니다 .

  • Integer: 16 바이트 결과는 예상 한 것보다 약간 나쁩니다. int값이 4 바이트에 불과할 수 있기 때문 입니다. 사용 Integer오버 I는 기본 유형으로 값을 저장할 수있는 경우에 비해 좀 300 %의 메모리를 요한다

  • Long: 16 바이트도 : 분명히 힙의 실제 객체 크기는 특정 CPU 유형에 대한 특정 JVM 구현에 의해 수행되는 낮은 수준의 메모리 정렬에 종속됩니다. a Long는 8 바이트의 Object 오버 헤드와 실제 long 값의 경우 8 바이트가 더 있는 것 같습니다 . 대조적으로, Integer사용하지 않는 JVM이 8 바이트 단어 경계에서 객체 정렬을 강제하기 때문에 사용되지 않는 4 바이트 구멍이 있습니다.

다른 용기들도 비용이 많이 든다 :

  • 다차원 배열 : 또 다른 놀라움을 제공합니다.
    개발자는 일반적으로 다음과 같은 구문을 사용합니다.int[dim1][dim2] 수치 및 과학 컴퓨팅과 합니다.

    에서 int[dim1][dim2]배열 인스턴스, 모든 중첩 된 int[dim2]배열은이다 Object그 자체이다. 각각은 일반적인 16 바이트 배열 오버 헤드를 추가합니다. 삼각형 또는 거친 배열이 필요하지 않은 경우 순수한 오버 헤드를 나타냅니다. 배열 치수가 크게 다를 때 영향이 커집니다.

    예를 들어, int[128][2]인스턴스는 3,600 바이트를 사용합니다. int[256]인스턴스가 사용 하는 1,040 바이트 (동일 용량)는 3,600 바이트가 246 %의 오버 헤드를 나타냅니다. 의 극단적 인 경우 byte[256][1]오버 헤드 계수는 거의 19입니다! 동일한 구문이 스토리지 오버 헤드를 추가하지 않는 C / C ++ 상황과 비교하십시오.

  • String: String의 메모리 증가는 내부 문자 배열의 증가를 추적합니다. 그러나 String클래스는 또 다른 24 바이트의 오버 헤드를 추가합니다.

    비어 있지 않은 String크기가 10 자 이하인 경우 유용한 페이로드 (각 문자에 2 바이트 + 길이에 4 바이트)에 대한 추가 오버 헤드 비용의 범위는 100-400 %입니다.

조정

예제 객체를 고려하십시오 .

class X {                      // 8 bytes for reference to the class definition
   int a;                      // 4 bytes
   byte b;                     // 1 byte
   Integer c = new Integer();  // 4 bytes for a reference
}

순진 합계는 인스턴스가 X17 바이트를 사용함을 나타냅니다. 그러나 정렬 (패딩이라고도 함)으로 인해 JVM은 8 바이트의 배수로 메모리를 할당하므로 17 바이트 대신 24 바이트를 할당합니다.


int [128] [6] : 6 개 정수 128 개 배열-총 768 개 정수, 3072 바이트 데이터 + 2064 바이트 개체 오버 헤드 = 총 5166 바이트. int [256] : 총 256 int이므로 비교할 수 없습니다. int [768] : 3072 바이트의 데이터 + 16 바이트 오버 헤드 – 2D 어레이 공간의 약 3/5 – 오버 헤드가 246 %가 아닙니다!
JeeBee

아, 원래 기사는 int [128] [6]가 아닌 int [128] [2]를 사용했습니다. 어떻게 바뀌 었는지 궁금합니다. 또한 극단적 인 예가 다른 이야기를 할 수 있음을 보여줍니다.
JeeBee

2
64 비트 JVM에서 오버 헤드는 16 바이트입니다.
Tim Cooper

3
@AlexWien : 일부 가비지 수집 체계는 패딩과 별 개인 최소 객체 크기를 부과 할 수 있습니다. 가비지 콜렉션 중에 오브젝트가 이전 위치에서 새 위치로 복사되면 이전 위치는 오브젝트의 데이터를 더 이상 보유 할 필요는 없지만 새 위치에 대한 참조를 보유해야합니다. 또한 객체의 이전 위치에 대한 참조를 저장해야 할 수도 있는데, 여기서 첫 번째 참조가 발견되고 이전 객체 내에서 해당 참조의 오프셋이 표시됩니다 (이전 객체는 아직 처리되지 않은 참조를 포함 할 수 있기 때문).
supercat

2
@AlexWien : 가비지 수집기의 장부 유지 정보를 보관하기 위해 개체의 이전 위치에 메모리를 사용하면 해당 목적을 위해 다른 메모리를 할당 할 필요는 없지만 필요한 경우보다 더 큰 최소 개체 크기를 부과 할 수 있습니다. 적어도 하나의 .NET 가비지 수집기 버전이이 방법을 사용한다고 생각합니다. 일부 Java 가비지 수집기에서도 그렇게 할 수 있습니다.
supercat

34

아키텍처 / jdk에 따라 다릅니다. 최신 JDK 및 64 비트 아키텍처의 경우 객체에는 12 바이트 헤더와 8 바이트 패딩이 있으므로 최소 객체 크기는 16 바이트입니다. Java Object Layout 이라는 도구 를 사용하여 크기를 결정하고 엔티티의 오브젝트 레이아웃 및 내부 구조에 대한 세부 사항을 얻거나 클래스 참조로이 정보를 추측 할 수 있습니다. 내 환경에서 정수의 출력 예 :

Running 64-bit HotSpot VM.
Using compressed oop with 3-bit shift.
Using compressed klass with 3-bit shift.
Objects are 8 bytes aligned.
Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

java.lang.Integer object internals:
 OFFSET  SIZE  TYPE DESCRIPTION                    VALUE
      0    12       (object header)                N/A
     12     4   int Integer.value                  N/A
Instance size: 16 bytes (estimated, the sample instance is not available)
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

따라서 Integer의 경우 인스턴스 크기는 16 바이트입니다. 헤더 바로 다음과 패딩 경계 바로 앞에 4 바이트 int가 압축되기 때문입니다.

코드 샘플 :

import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.util.VMSupport;

public static void main(String[] args) {
    System.out.println(VMSupport.vmDetails());
    System.out.println(ClassLayout.parseClass(Integer.class).toPrintable());
}

maven을 사용하는 경우 JOL을 얻으려면 다음을 수행하십시오.

<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.3.2</version>
</dependency>

28

각 객체에는 관련 모니터 및 유형 정보와 필드 자체에 대한 특정 오버 헤드가 있습니다. 그 외에도 필드를 거의 배치 할 수는 있지만 JVM은 적합하다고 생각합니다 (그러나 믿는다) . 또 다른 대답에서 볼 수 있듯이 적어도 일부 JVM은 상당히 단단히 포장됩니다. 다음과 같은 수업을 고려하십시오.

public class SingleByte
{
    private byte b;
}

vs

public class OneHundredBytes
{
    private byte b00, b01, ..., b99;
}

32 비트 JVM에서 100 개의 인스턴스가 SingleByte1200 바이트 (패딩 / 정렬로 인해 필드에 8 바이트의 오버 헤드 + 4 바이트)를 취할 것으로 예상 합니다. 하나의 인스턴스가 OneHundredBytes108 바이트, 오버 헤드, 100 바이트를 차지할 것으로 예상 됩니다. JVM마다 확실히 다를 수 있습니다. 하나의 구현으로 필드를 압축하지 않기로 결정할 수 있습니다OneHundredBytes 408 바이트 (= 8 바이트 오버 헤드 + 4 * 100 정렬 / 패딩 바이트)가 필요합니다. 64 비트 JVM에서 오버 헤드도 더 클 수 있습니다 (확실하지 않음).

편집 : 아래의 의견을 참조하십시오; 분명히 핫스팟은 32 개가 아닌 8 바이트의 경계로 채워 지므로 각 인스턴스는 SingleByte16 바이트를 차지합니다.

어느 쪽이든, "단일 큰 객체"는 간단한 작은 경우에 여러 개의 작은 객체만큼 효율적입니다.


9
핫스팟 컴파일러는 모든 것을 8의 배수로 반올림하므로 실제로 단일 바이트의 단일 바이트 인스턴스는 Sun JVM에서 16 바이트, 즉 8 바이트 오버 헤드, 필드는 4 바이트, 객체 패딩은 4 바이트가됩니다.
Paul Wagland

6

프로그램의 총 사용 / 여유 메모리는 프로그램을 통해 얻을 수 있습니다.

java.lang.Runtime.getRuntime();

런타임에는 메모리와 관련된 몇 가지 방법이 있습니다. 다음 코딩 예제는 사용법을 보여줍니다.

package test;

 import java.util.ArrayList;
 import java.util.List;

 public class PerformanceTest {
     private static final long MEGABYTE = 1024L * 1024L;

     public static long bytesToMegabytes(long bytes) {
         return bytes / MEGABYTE;
     }

     public static void main(String[] args) {
         // I assume you will know how to create a object Person yourself...
         List < Person > list = new ArrayList < Person > ();
         for (int i = 0; i <= 100000; i++) {
             list.add(new Person("Jim", "Knopf"));
         }
         // Get the Java runtime
         Runtime runtime = Runtime.getRuntime();
         // Run the garbage collector
         runtime.gc();
         // Calculate the used memory
         long memory = runtime.totalMemory() - runtime.freeMemory();
         System.out.println("Used memory is bytes: " + memory);
         System.out.println("Used memory is megabytes: " + bytesToMegabytes(memory));
     }
 }

6

모든 객체는 32 비트 시스템에서 16 바이트, 64 비트 시스템에서 24 바이트의 오버 헤드가있는 것으로 보입니다.

http://algs4.cs.princeton.edu/14analysis/ 는 좋은 정보원입니다. 많은 좋은 것들 중 하나의 예는 다음과 같습니다.

여기에 이미지 설명을 입력하십시오

http://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-efficient-java-tutorial.pdf 는 다음과 같이 매우 유익합니다.

여기에 이미지 설명을 입력하십시오


"모든 개체는 32 비트 시스템에서 16 바이트 (64 비트 시스템에서 24 바이트)의 오버 헤드를 갖는 것으로 보입니다." 적어도 현재 JDK에 대해서는 정확하지 않습니다. 정수 예제에 대한 내 대답 을 살펴보십시오 . 64 비트 시스템 및 최신 JDK의 헤더에 대한 오브젝트 오버 헤드는 12 바이트 이상입니다. 패딩으로 인해 더 많을 수 있으며 객체의 실제 필드 레이아웃에 따라 다릅니다.
Dmitry Spikhalskiy

메모리 효율적인 Java 튜토리얼에 대한 두 번째 링크는 죽은 것 같습니다. "Forbidden"이 표시됩니다.
tsleyson

6

하나의 속성을 가진 100 개의 속성과 100 개의 속성을 가진 하나의 객체가 사용하는 메모리 공간이 각각 하나의 속성을 가지고 있습니까?

아니.

객체에 얼마나 많은 메모리가 할당됩니까?

  • 오버 헤드는 32 비트에서 8 바이트, 64 비트에서 12 바이트입니다. 그런 다음 4 바이트 (32 비트) 또는 8 바이트 (64 비트)의 배수로 올림했습니다.

속성을 추가 할 때 얼마나 많은 공간이 사용됩니까?

  • 속성은 1 바이트 (바이트)로부터 8 바이트 (긴 / 배)의 범위이지만 참조 어느 따라 4 바이트 또는 8 바이트 하지 -Xmx은 그것의 32 비트 또는 64 비트인지 오히려 여부 <은 32 GB> = 32GB의 상 : 전형적인 64 -bit JVM에는 힙이 32Gb 미만인 경우 참조를 4 바이트로 압축하는 "-UseCompressedOops"라는 최적화가 있습니다.

1
문자는 8 비트가 아닌 16 비트입니다.
comonad

바로 당신입니다. 누군가가 내 원래 답변을 편집 한 것 같습니다
Jayen

5

아니요, 객체를 등록하는 데 약간의 메모리가 필요합니다. 속성이 1 인 100 개의 개체는 더 많은 메모리를 사용합니다.


4

질문은 매우 광범위 할 것입니다.

클래스 변수에 따라 다르거 나 java에서 상태 메모리 사용으로 호출 할 수 있습니다.

또한 헤더 및 참조에 대한 추가 메모리 요구 사항이 있습니다.

Java 객체가 사용하는 힙 메모리에는 다음이 포함됩니다.

  • 크기에 따른 프리미티브 필드의 메모리 (프리미티브 유형의 크기는 아래 참조);

  • 참조 필드 용 메모리 (각 4 바이트);

  • 몇 바이트의 "하우스 키핑"정보로 구성된 객체 헤더;

Java의 객체에는 객체의 클래스 기록, 객체에 도달 할 수 있는지 여부, 현재 동기화 잠금 상태 등의 상태 플래그와 같은 "하우스 키핑"정보가 필요합니다.

Java 객체 헤더 크기는 32 및 64 비트 jvm에 따라 다릅니다.

이것들이 주요 메모리 소비자이지만 jvm은 때로는 코드 정렬과 같은 추가 필드가 필요합니다.

기본 유형의 크기

부울 및 바이트 -1

char & short -2

int & float -4

롱 & 더블 -8


독자들은 또한이 논문이 매우 빛을 발하는
quellish



1

아니요, 100 개의 작은 물체에는 큰 물체보다 더 많은 정보 (메모리)가 필요합니다.


0

소비되는 메모리 양에 대한 규칙은 JVM 구현 및 CPU 아키텍처 (예 : 32 비트 대 64 비트)에 따라 다릅니다.

SUN JVM에 대한 자세한 규칙은 이전 블로그를 확인하십시오.

감사합니다, Markus


필자는 Sun Java 1.6 64 비트를 확신합니다. 일반 객체 + 4 패딩 = 16에는 12 바이트가 필요합니다. 개체 + 하나의 정수 필드 = 12 + 4 = 16
AlexWien

블로그를 종료 했습니까?
Johan Boulé

SAP 블로그가 어떻게 움직 였는지 확실하지 않습니다. 그것의 대부분은 여기에서 찾을 수 있습니다 kohlerm.blogspot.com
kohlerm을
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.