언어 및 도구들 공부/Java

Java 에서의 배열과 메모리 점유 2

Dr.Mlem 2024. 9. 22. 22:00

Q4.
32비트 윈도우 기준, 배열 자체의 메모리 점유량은 4byte라고 했는데, 이게 틀린말인거야?


네, 정확히 말씀드리자면 배열 자체의 메모리 점유량은 4바이트가 아닙니다. 사용자가 언급하신 4바이트는 **배열에 대한 참조(reference)**의 크기를 의미합니다. 자바에서 배열 변수는 실제 배열 객체를 가리키는 참조를 저장하며, 32비트 JVM에서는 이 참조가 4바이트를 차지합니다. 그러나 배열 객체 자체는 참조 외에도 추가적인 메모리를 소비합니다.

배열의 메모리 구성 요소

자바에서 배열은 객체로 취급되므로, 배열 객체는 다음과 같은 구성 요소로 메모리를 차지합니다:

  1. 객체 헤더 (Object Header)
    • Mark Word: 객체의 동기화 정보, 해시 코드 등을 저장 (4바이트)
    • Class Pointer: 객체의 클래스 메타데이터에 대한 참조 (4바이트)
    • 배열 길이 (Array Length): 배열의 길이를 저장 (4바이트)
    • 총합: 약 12바이트
  2. 배열 요소 (Array Elements)
    • 각 요소의 데이터 타입에 따라 크기가 다릅니다.
    • 예를 들어, int는 4바이트, double은 8바이트를 차지합니다.
    • 배열의 크기가 5라면, int[]의 경우 5 * 4바이트 = 20바이트, double[]의 경우 5 * 8바이트 = 40바이트
  3. 패딩 (Padding)
    • JVM은 메모리 정렬을 위해 패딩을 추가할 수 있습니다. 일반적으로 객체는 8바이트 단위로 정렬되므로, 총 메모리 사용량이 8바이트 배수가 되도록 패딩이 추가될 수 있습니다.

32비트 JVM에서의 메모리 점유량 예시

1. int[] integer1 = new int[5];

  • 객체 헤더: 12바이트
  • 배열 요소 데이터: 5 * 4바이트 = 20바이트
  • 패딩: 전체 크기를 8바이트 단위로 맞추기 위해 추가 (예: 총 32바이트로 정렬)

총 메모리 사용량: 약 32바이트

2. double[] float1 = new double[5];

  • 객체 헤더: 12바이트
  • 배열 요소 데이터: 5 * 8바이트 = 40바이트
  • 패딩: 전체 크기를 8바이트 단위로 맞추기 위해 추가 (예: 총 56바이트로 정렬)

총 메모리 사용량: 약 56바이트

참조와 실제 배열 객체의 차이

  • 배열 참조 (Reference): 변수 자체는 배열 객체를 가리키는 참조로, 32비트 JVM에서는 4바이트를 차지합니다.

    int[] integer1 = new int[5];

    여기서 integer1 변수는 4바이트의 참조를 저장하며, 실제 배열 객체는 별도의 메모리를 차지합니다.

  • 배열 객체 (Array Object): 배열 객체 자체는 객체 헤더와 배열 요소로 인해 더 많은 메모리를 차지합니다.

    int[] integer1 = new int[5];

    위 코드에서 integer1이 가리키는 실제 배열 객체는 약 32바이트를 차지하게 됩니다 (객체 헤더 12바이트 + 배열 요소 20바이트).

시각적인 예시

public class ArrayMemoryExample {
    public static void main(String[] args) {
        // 배열 참조 변수는 4바이트를 차지
        int[] integer1 = new int[5];  // integer1 참조는 4바이트
        
        // 실제 배열 객체는 객체 헤더와 데이터로 메모리를 차지
        // 객체 헤더: 12바이트
        // 배열 요소: 5 * 4바이트 = 20바이트
        // 패딩: 총 32바이트로 정렬 (예상)
        
        // 배열 참조 자체는 작은 메모리 공간을 차지하지만,
        // 배열 객체는 더 많은 메모리를 소비함을 유의하세요.
    }
}

요약

  • 배열 참조 (Reference): 32비트 JVM에서 4바이트
  • 배열 객체 (Array Object):
    • 객체 헤더: 12바이트
    • 배열 요소: 데이터 타입에 따라 다름 (int[]의 경우 20바이트)
    • 패딩: 정렬에 따라 추가 (예: 총 32바이트)

따라서, 배열 자체의 메모리 점유량은 참조의 4바이트가 아니라 객체 헤더와 배열 요소로 구성된 더 큰 크기를 차지하게 됩니다. 사용자가 언급한 4바이트는 배열에 대한 참조의 크기이며, 실제 배열 객체는 더 많은 메모리를 소비합니다.



Q5.
class pointer는 정수형으로 지정되어서 4byte를 점유한다라고 말을 하면 맞는 말이야?


**"Class Pointer는 정수형으로 지정되어서 4바이트를 점유한다"**라는 표현은 약간의 오해의 소지가 있습니다. 정확히 말하면, **클래스 포인터(Class Pointer)**는 정수형 데이터 타입이 아니라 **메모리 주소를 가리키는 참조(Reference)**입니다. 하지만, 32비트 JVM 환경에서는 이 참조가 4바이트를 차지한다는 점에서 정수형 데이터 타입과 크기가 일치하는 것처럼 보일 수 있습니다.

1. 클래스 포인터(Class Pointer)란?

클래스 포인터는 자바 객체가 어떤 클래스에 속해 있는지를 나타내는 참조입니다. 이는 객체의 메타데이터(예: 메서드, 필드 정보 등)에 접근할 수 있도록 도와줍니다. 클래스 포인터는 JVM 내부에서 객체의 클래스 메타데이터를 가리키는 포인터 역할을 합니다.

2. 클래스 포인터의 메모리 점유량

32비트 JVM 환경:

  • 클래스 포인터의 크기: 4 바이트
    • 32비트 시스템에서는 메모리 주소가 **32비트(4바이트)**로 표현됩니다. 따라서 클래스 포인터는 4바이트를 차지합니다.

64비트 JVM 환경:

  • Compressed OOPs 사용 시:
    • 클래스 포인터의 크기: 4 바이트
    • 설명: 대부분의 64비트 JVM은 **Compressed Ordinary Object Pointers (Compressed OOPs)**를 사용하여 객체 포인터의 크기를 줄입니다. 이를 통해 클래스 포인터도 4바이트로 유지됩니다.
  • Compressed OOPs 미사용 시:
    • 클래스 포인터의 크기: 8 바이트
    • 설명: 압축을 사용하지 않으면, 메모리 주소가 **64비트(8바이트)**로 표현되므로 클래스 포인터도 8바이트를 차지합니다.

3. 클래스 포인터는 정수형 데이터 타입인가?

아니요, 클래스 포인터는 **정수형 데이터 타입(Integer)**이 아닙니다. 대신, **메모리 주소를 저장하는 포인터(참조)**입니다. 그러나 32비트 JVM에서는 이 포인터가 4바이트를 차지하기 때문에, 데이터 크기 측면에서는 32비트 정수와 동일하게 보일 수 있습니다. 하지만 그 용도와 의미는 다릅니다.

4. 클래스 포인터의 역할과 중요성

클래스 포인터는 다음과 같은 중요한 역할을 합니다:

  • 객체의 클래스 정보 접근: JVM이 객체의 메서드나 필드에 접근할 수 있도록 합니다.
  • 런타임 타입 정보 제공: 객체의 런타임 타입을 확인하고, 동적 바인딩 등을 지원합니다.
  • 가비지 컬렉션 지원: 객체의 메타데이터를 통해 가비지 컬렉션이 효율적으로 이루어질 수 있습니다.

5. 예시를 통한 이해

 

public class Example {
    public static void main(String[] args) {
        int[] integer1 = new int[5];
        double[] float1 = new double[5];
    }
}

위 코드에서 integer1과 float1은 각각 int[]와 double[] 타입의 배열을 참조합니다. 각 배열 객체는 다음과 같은 메모리 구성을 가집니다:

  • 객체 헤더(Object Header):
    • Mark Word: 동기화 정보, 해시 코드 등 (4바이트)
    • Class Pointer: 클래스 메타데이터를 가리키는 포인터 (32비트 JVM에서는 4바이트)
    • 배열 길이(Array Length): 배열의 길이를 저장 (4바이트)
    • 총합:12바이트
  • 배열 요소(Array Elements):
    • int[]의 경우: 5 * 4바이트 = 20바이트
    • double[]의 경우: 5 * 8바이트 = 40바이트
  • 패딩(Padding):
    • 메모리 정렬을 맞추기 위해 추가될 수 있는 여분의 바이트 (예: 전체 크기를 8바이트 배수로 맞춤)

총 메모리 사용량:

  • int[] integer1: 약 12바이트 (헤더) + 20바이트 (데이터) + 패딩 ≈ 32바이트
  • double[] float1: 약 12바이트 (헤더) + 40바이트 (데이터) + 패딩 ≈ 56바이트

6. 요약

  • 클래스 포인터는 정수형 데이터 타입이 아니라 메모리 주소를 가리키는 참조입니다.
  • 32비트 JVM에서는 클래스 포인터가 4바이트를 차지하지만, 이는 정수형과는 다른 개념입니다.
  • 64비트 JVM에서는 Compressed OOPs 사용 시 클래스 포인터가 4바이트, 미사용 시 8바이트를 차지합니다.
  • 클래스 포인터는 객체의 메타데이터에 접근하고, 객체의 상태를 관리하는 데 중요한 역할을 합니다.

따라서, 클래스 포인터가 4바이트를 점유한다는 것은 참조의 크기를 설명하는 것이지, 정수형 데이터 타입으로 지정되어 있다는 의미는 아닙니다. 이를 올바르게 이해하는 것이 중요합니다.