[java] Java에서 객체의 크기를 결정하는 가장 좋은 방법은 무엇입니까?

데이터 행 더미가있는 CSV 파일을 읽는 응용 프로그램이 있습니다. 사용자에게 데이터 유형에 따라 행 수에 대한 요약을 제공하지만 너무 많은 행의 데이터를 읽지 않아서 OutOfMemoryErrors가 발생하지 않도록하고 싶습니다 . 각 행은 객체로 변환됩니다. 프로그래밍 방식으로 해당 객체의 크기를 찾는 쉬운 방법이 있습니까? 큰 기본 유형과 객체 참조가 얼마나 큰지를 정의하는 참조가 VM있습니까?

지금은 최대 32,000 행 을 읽는 코드가 있지만 32MB 의 메모리를 사용할 때까지 가능한 한 많은 행을 읽는 코드를 갖고 싶습니다 . 어쩌면 그것은 다른 질문 일지 모르지만 여전히 알고 싶습니다.



답변

java.lang.instrument 패키지를 사용할 수 있습니다

이 클래스를 컴파일하여 JAR에 넣습니다.

import java.lang.instrument.Instrumentation;

public class ObjectSizeFetcher {
    private static Instrumentation instrumentation;

    public static void premain(String args, Instrumentation inst) {
        instrumentation = inst;
    }

    public static long getObjectSize(Object o) {
        return instrumentation.getObjectSize(o);
    }
}

에 다음을 추가하십시오 MANIFEST.MF.

Premain-Class: ObjectSizeFetcher

getObjectSize를 사용하십시오.

public class C {
    private int x;
    private int y;

    public static void main(String [] args) {
        System.out.println(ObjectSizeFetcher.getObjectSize(new C()));
    }
}

다음과 같이 호출하십시오.

java -javaagent:ObjectSizeFetcherAgent.jar C


답변

OpenJDK 프로젝트의 일부로 개발 된 도구 인 jol을 사용해야합니다 .

JOL (Java Object Layout)은 JVM에서 객체 레이아웃 구성표를 분석하는 작은 도구 상자입니다. 이러한 도구는 Unsafe, JVMTI 및 SA (Serviceability Agent)를 많이 사용하여 실제 객체 레이아웃, 풋 프린트 및 참조를 해독합니다. 따라서 힙 덤프, 사양 가정 등에 의존하는 다른 도구보다 JOL이 훨씬 정확합니다.

프리미티브, 참조 및 배열 요소의 크기를 얻으려면을 사용하십시오 VMSupport.vmDetails(). 64 비트 Windows에서 실행중인 Oracle JDK 1.8.0_40 (다음의 모든 예제에 사용)에서이 메소드는 다음을 리턴합니다.

Running 64-bit HotSpot VM.
Using compressed oop with 0-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]

ClassLayout.parseClass(Foo.class).toPrintable()(선택적으로 인스턴스를에 전달)를 사용하여 객체 인스턴스의 얕은 크기를 얻을 수 있습니다 toPrintable. 이것은 해당 클래스의 단일 인스턴스가 사용하는 공간입니다. 해당 클래스가 참조하는 다른 객체는 포함하지 않습니다. 여기 에는 개체 헤더, 필드 정렬 및 패딩에 대한 VM 오버 헤드 포함됩니다. 의 경우 java.util.regex.Pattern:

java.util.regex.Pattern object internals:
 OFFSET  SIZE        TYPE DESCRIPTION                    VALUE
      0     4             (object header)                01 00 00 00 (0000 0001 0000 0000 0000 0000 0000 0000)
      4     4             (object header)                00 00 00 00 (0000 0000 0000 0000 0000 0000 0000 0000)
      8     4             (object header)                cb cf 00 20 (1100 1011 1100 1111 0000 0000 0010 0000)
     12     4         int Pattern.flags                  0
     16     4         int Pattern.capturingGroupCount    1
     20     4         int Pattern.localCount             0
     24     4         int Pattern.cursor                 48
     28     4         int Pattern.patternLength          0
     32     1     boolean Pattern.compiled               true
     33     1     boolean Pattern.hasSupplementary       false
     34     2             (alignment/padding gap)        N/A
     36     4      String Pattern.pattern                (object)
     40     4      String Pattern.normalizedPattern      (object)
     44     4        Node Pattern.root                   (object)
     48     4        Node Pattern.matchRoot              (object)
     52     4       int[] Pattern.buffer                 null
     56     4         Map Pattern.namedGroups            null
     60     4 GroupHead[] Pattern.groupNodes             null
     64     4       int[] Pattern.temp                   null
     68     4             (loss due to the next object alignment)
Instance size: 72 bytes (reported by Instrumentation API)
Space losses: 2 bytes internal + 4 bytes external = 6 bytes total

을 사용하여 객체 인스턴스의 깊은 크기에 대한 요약보기를 얻을 수 있습니다 GraphLayout.parseInstance(obj).toFootprint(). 물론, 풋 프린트의 일부 오브젝트는 공유 될 수 있으며 (다른 오브젝트에서도 참조 될 수 있음), 해당 오브젝트가 가비지 콜렉션 될 때 재 확보 될 수있는 공간의 초과 근사치입니다. Pattern.compile("^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$")( 이 답변 에서 가져온) 결과에 대해 jol은 총 1840 바이트의 풋 프린트를보고하며 그 중 72 개만이 Pattern 인스턴스 자체입니다.

java.util.regex.Pattern instance footprint:
     COUNT       AVG       SUM   DESCRIPTION
         1       112       112   [C
         3       272       816   [Z
         1        24        24   java.lang.String
         1        72        72   java.util.regex.Pattern
         9        24       216   java.util.regex.Pattern$1
        13        24       312   java.util.regex.Pattern$5
         1        16        16   java.util.regex.Pattern$Begin
         3        24        72   java.util.regex.Pattern$BitClass
         3        32        96   java.util.regex.Pattern$Curly
         1        24        24   java.util.regex.Pattern$Dollar
         1        16        16   java.util.regex.Pattern$LastNode
         1        16        16   java.util.regex.Pattern$Node
         2        24        48   java.util.regex.Pattern$Single
        40                1840   (total)

대신을 사용 GraphLayout.parseInstance(obj).toPrintable()하면 jol은 참조하는 각 객체에 대한 주소, 크기, 유형, 값 및 필드 역 참조 경로를 알려줍니다. 진행중인 패턴 예제의 경우 다음을 얻을 수 있습니다. (주소는 실행간에 변경 될 수 있습니다.)

java.util.regex.Pattern object externals:
          ADDRESS       SIZE TYPE                             PATH                           VALUE
         d5e5f290         16 java.util.regex.Pattern$Node     .root.next.atom.next           (object)
         d5e5f2a0        120 (something else)                 (somewhere else)               (something else)
         d5e5f318         16 java.util.regex.Pattern$LastNode .root.next.next.next.next.next.next.next (object)
         d5e5f328      21664 (something else)                 (somewhere else)               (something else)
         d5e647c8         24 java.lang.String                 .pattern                       (object)
         d5e647e0        112 [C                               .pattern.value                 [^, [, a, -, z, A, -, Z, 0, -, 9, _, ., +, -, ], +, @, [, a, -, z, A, -, Z, 0, -, 9, -, ], +, \, ., [, a, -, z, A, -, Z, 0, -, 9, -, ., ], +, $]
         d5e64850        448 (something else)                 (somewhere else)               (something else)
         d5e64a10         72 java.util.regex.Pattern                                         (object)
         d5e64a58        416 (something else)                 (somewhere else)               (something else)
         d5e64bf8         16 java.util.regex.Pattern$Begin    .root                          (object)
         d5e64c08         24 java.util.regex.Pattern$BitClass .root.next.atom.val$rhs        (object)
         d5e64c20        272 [Z                               .root.next.atom.val$rhs.bits   [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
         d5e64d30         24 java.util.regex.Pattern$1        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs (object)
         d5e64d48         24 java.util.regex.Pattern$1        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs.val$rhs (object)
         d5e64d60         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs (object)
         d5e64d78         24 java.util.regex.Pattern$1        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$rhs (object)
         d5e64d90         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs (object)
         d5e64da8         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs.val$lhs.val$lhs (object)
         d5e64dc0         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs.val$lhs (object)
         d5e64dd8         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs        (object)
         d5e64df0         24 java.util.regex.Pattern$5        .root.next.atom                (object)
         d5e64e08         32 java.util.regex.Pattern$Curly    .root.next                     (object)
         d5e64e28         24 java.util.regex.Pattern$Single   .root.next.next                (object)
         d5e64e40         24 java.util.regex.Pattern$BitClass .root.next.next.next.atom.val$rhs (object)
         d5e64e58        272 [Z                               .root.next.next.next.atom.val$rhs.bits [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
         d5e64f68         24 java.util.regex.Pattern$1        .root.next.next.next.atom.val$lhs.val$lhs.val$lhs (object)
         d5e64f80         24 java.util.regex.Pattern$1        .root.next.next.next.atom.val$lhs.val$lhs.val$rhs (object)
         d5e64f98         24 java.util.regex.Pattern$5        .root.next.next.next.atom.val$lhs.val$lhs (object)
         d5e64fb0         24 java.util.regex.Pattern$1        .root.next.next.next.atom.val$lhs.val$rhs (object)
         d5e64fc8         24 java.util.regex.Pattern$5        .root.next.next.next.atom.val$lhs (object)
         d5e64fe0         24 java.util.regex.Pattern$5        .root.next.next.next.atom      (object)
         d5e64ff8         32 java.util.regex.Pattern$Curly    .root.next.next.next           (object)
         d5e65018         24 java.util.regex.Pattern$Single   .root.next.next.next.next      (object)
         d5e65030         24 java.util.regex.Pattern$BitClass .root.next.next.next.next.next.atom.val$rhs (object)
         d5e65048        272 [Z                               .root.next.next.next.next.next.atom.val$rhs.bits [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
         d5e65158         24 java.util.regex.Pattern$1        .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$lhs.val$lhs (object)
         d5e65170         24 java.util.regex.Pattern$1        .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$lhs.val$rhs (object)
         d5e65188         24 java.util.regex.Pattern$5        .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$lhs (object)
         d5e651a0         24 java.util.regex.Pattern$1        .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$rhs (object)
         d5e651b8         24 java.util.regex.Pattern$5        .root.next.next.next.next.next.atom.val$lhs.val$lhs (object)
         d5e651d0         24 java.util.regex.Pattern$5        .root.next.next.next.next.next.atom.val$lhs (object)
         d5e651e8         24 java.util.regex.Pattern$5        .root.next.next.next.next.next.atom (object)
         d5e65200         32 java.util.regex.Pattern$Curly    .root.next.next.next.next.next (object)
         d5e65220        120 (something else)                 (somewhere else)               (something else)
         d5e65298         24 java.util.regex.Pattern$Dollar   .root.next.next.next.next.next.next (object)

“(something else)”항목 은 힙에서이 객체 그래프의 일부가 아닌 다른 객체를 설명합니다 .

최고의 jol 문서는 jol 저장소 의 jol 샘플 입니다. 샘플은 일반적인 jol 조작을 보여주고 jol을 사용하여 VM 및 가비지 콜렉터 내부를 분석하는 방법을 보여줍니다.


답변

실수로 jdk에 이미있는 자바 클래스 “jdk.nashorn.internal.ir.debug.ObjectSizeCalculator”를 발견했습니다. 이는 사용하기 쉽고 객체의 크기를 결정하는 데 매우 유용한 것으로 보입니다.

System.out.println(ObjectSizeCalculator.getObjectSize(new gnu.trove.map.hash.TObjectIntHashMap<String>(12000, 0.6f, -1)));
System.out.println(ObjectSizeCalculator.getObjectSize(new HashMap<String, Integer>(100000)));
System.out.println(ObjectSizeCalculator.getObjectSize(3));
System.out.println(ObjectSizeCalculator.getObjectSize(new int[]{1, 2, 3, 4, 5, 6, 7 }));
System.out.println(ObjectSizeCalculator.getObjectSize(new int[100]));

결과 :

164192
48
16
48
416


답변

몇 년 전 Javaworld는 복합 및 잠재적으로 중첩 된 Java 객체의 크기를 결정하는 데 관한 기사를 가지고 있었으며 기본적으로 Java 에서 sizeof () 구현을 작성하는 과정을 안내합니다. 이 접근 방식은 기본적으로 사람들이 기본적으로 프리미티브 및 일반적인 Java 객체의 크기를 식별 한 다음 전체 크기를 계산하기 위해 객체 그래프를 재귀 적으로 걷는 방법에 해당 지식을 적용하는 다른 작업을 기반으로합니다.

클래스의 무대 뒤에서 일어나는 일 때문에 항상 네이티브 C 구현보다 다소 덜 정확하지만 좋은 지표가되어야합니다.

또는 sizeof () 구현으로 Java5 라이브러리를 제공하는 sizeof 라는 적절한 SourceForge 프로젝트 .

PS 직렬화 방법을 사용하지 마십시오. 직렬화 된 객체의 크기와 라이브시 소비하는 메모리 양 사이에는 상관이 없습니다.


답변

먼저 “객체의 크기”는 Java에서 잘 정의 된 개념이 아닙니다. 멤버, 오브젝트 및 참조하는 모든 오브젝트 (참조 그래프)만으로 오브젝트 자체를 의미 할 수 있습니다. 메모리의 크기 또는 디스크의 크기를 의미 할 수 있습니다. 그리고 JVM은 문자열과 같은 것을 최적화 할 수 있습니다.

따라서 올바른 올바른 방법은 프로파일 러 ( YouKit 사용 )로 JVM에 요청 하는 것입니다.

그러나 위의 설명에서 각 행이 독립적이며 큰 종속성 트리가없는 것처럼 들리므로 직렬화 방법은 대부분의 JVM에서 좋은 근사치 일 것입니다. 가장 쉬운 방법은 다음과 같습니다.

 Serializable ser;
 ByteArrayOutputStream baos = new ByteArrayOutputStream();
 ObjectOutputStream oos = new ObjectOutputStream(baos);
 oos.writeObject(ser);
 oos.close();
 return baos.size();

당신은 일반적인 참조가 개체가있는 경우이 것을 기억 하지 않습니다 정확한 결과를 제공하고, 직렬화의 크기는 항상 메모리 크기와 일치하지 않습니다,하지만 좋은 근사이다. ByteArrayOutputStream 크기를 적절한 값으로 초기화하면 코드가 조금 더 효율적입니다.


답변

JVM에서 사용되는 메모리 양과 사용 가능한 양을 알고 싶다면 다음과 같이 시도하십시오.

// Get current size of heap in bytes
long heapSize = Runtime.getRuntime().totalMemory();

// Get maximum size of heap in bytes. The heap cannot grow beyond this size.
// Any attempt will result in an OutOfMemoryException.
long heapMaxSize = Runtime.getRuntime().maxMemory();

// Get amount of free memory within the heap in bytes. This size will increase
// after garbage collection and decrease as new objects are created.
long heapFreeSize = Runtime.getRuntime().freeMemory();

편집 : 나는 질문 작성자가 “32MB의 메모리를 사용할 때까지 가능한 한 많은 행을 읽습니다.”를 처리하는 논리를 갖고 싶다고 말했기 때문에 이것이 도움이 될 것이라고 생각했습니다.


답변

트위터에서 일할 때 깊은 물체 크기를 계산하는 유틸리티를 작성했습니다. 서로 다른 메모리 모델 (32 비트, 압축 된 oop, 64 비트), 패딩, 서브 클래스 패딩을 고려하여 순환 데이터 구조 및 배열에서 올바르게 작동합니다. 이 하나의 .java 파일을 컴파일하면됩니다. 외부 종속성이 없습니다.

https://github.com/twitter/commons/blob/master/src/java/com/twitter/common/objectsize/ObjectSizeCalculator.java