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

StackOverflow https://stackoverflow.com/questions/52353

  •  09-06-2019
  •  | 
  •  

문제

예를 들어, 데이터 행이 쌓여 있는 CSV 파일을 읽을 수 있는 애플리케이션이 있다고 가정해 보겠습니다.사용자에게 데이터 유형에 따라 행 수에 대한 요약을 제공하지만 너무 많은 행의 데이터를 읽지 않도록 하고 싶습니다. OutOfMemoryError에스.각 행은 개체로 변환됩니다.프로그래밍 방식으로 해당 개체의 크기를 알아내는 쉬운 방법이 있습니까?기본 유형 및 객체 참조의 크기를 정의하는 참조가 있습니까? 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(Java Object Layout)은 JVM의 객체 레이아웃 체계를 분석하는 작은 도구 상자입니다.이러한 도구는 Unsafe, JVMTI 및 SA(서비스 가능성 에이전트)를 많이 사용하여 실제 개체 레이아웃, 공간 및 참조를 디코딩합니다.이로 인해 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)

"(다른 것)" 항목 이 개체 그래프의 일부가 아닌 힙의 다른 개체를 설명합니다..

최고의 jol 문서는 졸 샘플 jol 저장소에 있습니다.샘플은 일반적인 jol 작업을 보여주고 jol을 사용하여 VM 및 가비지 수집기 내부를 분석하는 방법을 보여줍니다.

몇 년 전 Javaworld는 복합 및 잠재적으로 중첩된 Java 객체의 크기를 결정하는 방법에 대한 기사, 기본적으로 Java에서 sizeof() 구현을 만드는 과정을 안내합니다.이 접근 방식은 기본적으로 사람들이 프리미티브 및 일반적인 Java 개체의 크기를 실험적으로 식별한 다음 해당 지식을 개체 그래프를 반복적으로 탐색하여 전체 크기를 계산하는 방법에 적용하는 다른 작업을 기반으로 합니다.

클래스 뒤에서 일어나는 일 때문에 항상 기본 C 구현보다 정확도가 다소 떨어지겠지만 좋은 지표가 되어야 합니다.

또는 적절하게 호출되는 SourceForge 프로젝트 크기 이는 sizeof() 구현이 포함된 Java5 라이브러리를 제공합니다.

추신직렬화 접근 방식을 사용하지 마십시오. 직렬화된 객체의 크기와 활성 상태에서 소비하는 메모리 양 사이에는 상관관계가 없습니다.

첫째, "객체의 크기"는 Java에서 잘 정의된 개념이 아닙니다.개체 자체, 해당 멤버, 개체 및 개체가 참조하는 모든 개체(참조 그래프)를 의미할 수 있습니다.메모리 크기 또는 디스크 크기를 의미할 수 있습니다.그리고 JVM은 문자열과 같은 것을 최적화할 수 있습니다.

따라서 유일한 올바른 방법은 좋은 프로파일러를 사용하여 JVM에 요청하는 것입니다(저는 YourKit) 이는 아마도 당신이 원하는 것이 아닐 것입니다.

그러나 위의 설명에 따르면 각 행은 자체 포함되어 있고 큰 종속성 트리가 없는 것처럼 들리므로 직렬화 방법은 아마도 대부분의 JVM에 대한 좋은 근사치일 것입니다.이를 수행하는 가장 쉬운 방법은 다음과 같습니다.

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

공통 참조가 있는 객체가 있는 경우 다음을 기억하세요. ~하지 않을 것이다 올바른 결과를 제공하며 직렬화 크기가 항상 메모리 크기와 일치하지는 않지만 좋은 근사치입니다.ByteArrayOutputStream 크기를 적절한 값으로 초기화하면 코드가 좀 더 효율적이 됩니다.

실수로 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

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의 메모리를 사용할 때까지 가능한 한 많은 행을 읽는" 논리를 갖고 싶다고 말했기 때문에 이것이 도움이 될 것이라고 생각했습니다.

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

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

다른 답변의 대부분은 얕은 크기를 제공합니다.키나 값이 없는 HashMap의 크기. 이는 원하는 것과 다를 수 있습니다.

jamm 프로젝트는 위의 java.lang.instrumentation 패키지를 사용하지만 트리를 탐색하므로 깊은 메모리 사용을 제공할 수 있습니다.

new MemoryMeter().measureDeep(myHashMap);

https://github.com/jbellis/jamm

MemoryMeter를 사용하려면 "-javaagent:/jamm.jar"로 JVM을 시작하십시오.

반사를 사용하여 물체를 걸어야 합니다.다음과 같이 주의하세요.

  • 객체를 할당하는 것만으로도 JVM에 약간의 오버헤드가 발생합니다.양은 JVM에 따라 다르므로 이 값을 매개변수로 만들 수 있습니다.최소한 상수(8바이트?)로 만들고 할당된 항목에 적용하십시오.
  • 으니까 byte 이론적으로 1바이트라고 해서 메모리에 1바이트만 필요하다는 의미는 아닙니다.
  • 객체 참조에 루프가 있으므로 HashMap 아니면 그런 것 객체 같음을 비교자로 사용 무한 루프를 없애기 위해.

@jodonnell:솔루션의 단순성은 마음에 들지만 많은 개체가 직렬화 가능하지 않고(따라서 예외가 발생함) 필드가 일시적일 수 있으며 개체가 표준 메서드를 재정의할 수 있습니다.

도구로 측정하거나 손으로 추정해야 하며 사용하는 JVM에 따라 다릅니다.

객체당 고정된 오버헤드가 있습니다.JVM에 따라 다르지만 일반적으로 40바이트로 추정됩니다.그다음에는 학급의 구성원을 살펴보아야 합니다.객체 참조는 32비트(64비트) JVM에서 4(8)바이트입니다.기본 유형은 다음과 같습니다.

  • 부울 및 바이트:1바이트
  • 문자 및 짧은:2바이트
  • int 및 float:4 바이트
  • 길고 두 배:8바이트

배열은 동일한 규칙을 따릅니다.즉, 개체 참조이므로 개체에서 4(또는 8)바이트를 사용하고 해당 길이에 해당 요소의 크기를 곱합니다.

호출을 통해 프로그래밍 방식으로 수행하려고 합니다. Runtime.freeMemory() 가비지 수집기에 대한 비동기 호출 등으로 인해 많은 정확성을 제공하지 않습니다.-Xrunhprof 또는 기타 도구를 사용하여 힙을 프로파일링하면 가장 정확한 결과를 얻을 수 있습니다.

그만큼 java.lang.instrument.Instrumentation 클래스는 Java 객체의 크기를 얻는 좋은 방법을 제공하지만 이를 위해서는 premain Java 에이전트로 프로그램을 실행하십시오.에이전트가 필요하지 않고 애플리케이션에 더미 Jar 에이전트를 제공해야 하는 경우 이는 매우 지루합니다.

그래서 나는 다음을 사용하여 대체 솔루션을 얻었습니다. Unsafe 의 수업 sun.misc.따라서 프로세서 아키텍처에 따른 객체 힙 정렬을 고려하고 최대 필드 오프셋을 계산하면 Java 객체의 크기를 측정할 수 있습니다.아래 예에서는 보조 클래스를 사용합니다. UtilUnsafe 에 대한 참조를 얻으려면 sun.misc.Unsafe 물체.

private static final int NR_BITS = Integer.valueOf(System.getProperty("sun.arch.data.model"));
private static final int BYTE = 8;
private static final int WORD = NR_BITS/BYTE;
private static final int MIN_SIZE = 16; 

public static int sizeOf(Class src){
    //
    // Get the instance fields of src class
    // 
    List<Field> instanceFields = new LinkedList<Field>();
    do{
        if(src == Object.class) return MIN_SIZE;
        for (Field f : src.getDeclaredFields()) {
            if((f.getModifiers() & Modifier.STATIC) == 0){
                instanceFields.add(f);
            }
        }
        src = src.getSuperclass();
    }while(instanceFields.isEmpty());
    //
    // Get the field with the maximum offset
    //  
    long maxOffset = 0;
    for (Field f : instanceFields) {
        long offset = UtilUnsafe.UNSAFE.objectFieldOffset(f);
        if(offset > maxOffset) maxOffset = offset; 
    }
    return  (((int)maxOffset/WORD) + 1)*WORD; 
}
class UtilUnsafe {
    public static final sun.misc.Unsafe UNSAFE;

    static {
        Object theUnsafe = null;
        Exception exception = null;
        try {
            Class<?> uc = Class.forName("sun.misc.Unsafe");
            Field f = uc.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            theUnsafe = f.get(uc);
        } catch (Exception e) { exception = e; }
        UNSAFE = (sun.misc.Unsafe) theUnsafe;
        if (UNSAFE == null) throw new Error("Could not obtain access to sun.misc.Unsafe", exception);
    }
    private UtilUnsafe() { }
}

또한 메모리 측정기 도구(이전에는 구글 코드, 지금 GitHub), 이는 간단하고 상업 친화적인 버전으로 게시됩니다. 아파치 2.0 라이센스, 에서 논의한 바와 같이 비슷한 질문.

또한 메모리 바이트 소비를 측정하려면 Java 인터프리터에 대한 명령줄 인수가 필요하지만 적어도 내가 사용한 시나리오에서는 정상적으로 작동하는 것 같습니다.

다음은 압축된 OOP로 32비트, 64비트 및 64비트를 처리하기 위해 연결된 예제 중 일부를 사용하여 만든 유틸리티입니다.그것은 사용한다 sun.misc.Unsafe.

그것은 사용한다 Unsafe.addressSize() 네이티브 포인터의 크기를 얻으려면 Unsafe.arrayIndexScale( Object[].class ) Java 참조의 크기.

알려진 클래스의 필드 오프셋을 사용하여 객체의 기본 크기를 계산합니다.

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.IdentityHashMap;
import java.util.Stack;
import sun.misc.Unsafe;

/** Usage: 
 * MemoryUtil.sizeOf( object )
 * MemoryUtil.deepSizeOf( object )
 * MemoryUtil.ADDRESS_MODE
 */
public class MemoryUtil
{
    private MemoryUtil()
    {
    }

    public static enum AddressMode
    {
        /** Unknown address mode. Size calculations may be unreliable. */
        UNKNOWN,
        /** 32-bit address mode using 32-bit references. */
        MEM_32BIT,
        /** 64-bit address mode using 64-bit references. */
        MEM_64BIT,
        /** 64-bit address mode using 32-bit compressed references. */
        MEM_64BIT_COMPRESSED_OOPS
    }

    /** The detected runtime address mode. */
    public static final AddressMode ADDRESS_MODE;

    private static final Unsafe UNSAFE;

    private static final long ADDRESS_SIZE; // The size in bytes of a native pointer: 4 for 32 bit, 8 for 64 bit
    private static final long REFERENCE_SIZE; // The size of a Java reference: 4 for 32 bit, 4 for 64 bit compressed oops, 8 for 64 bit
    private static final long OBJECT_BASE_SIZE; // The minimum size of an Object: 8 for 32 bit, 12 for 64 bit compressed oops, 16 for 64 bit
    private static final long OBJECT_ALIGNMENT = 8;

    /** Use the offset of a known field to determine the minimum size of an object. */
    private static final Object HELPER_OBJECT = new Object() { byte b; };


    static
    {
        try
        {
            // Use reflection to get a reference to the 'Unsafe' object.
            Field f = Unsafe.class.getDeclaredField( "theUnsafe" );
            f.setAccessible( true );
            UNSAFE = (Unsafe) f.get( null );

            OBJECT_BASE_SIZE = UNSAFE.objectFieldOffset( HELPER_OBJECT.getClass().getDeclaredField( "b" ) );

            ADDRESS_SIZE = UNSAFE.addressSize();
            REFERENCE_SIZE = UNSAFE.arrayIndexScale( Object[].class );

            if( ADDRESS_SIZE == 4 )
            {
                ADDRESS_MODE = AddressMode.MEM_32BIT;
            }
            else if( ADDRESS_SIZE == 8 && REFERENCE_SIZE == 8 )
            {
                ADDRESS_MODE = AddressMode.MEM_64BIT;
            }
            else if( ADDRESS_SIZE == 8 && REFERENCE_SIZE == 4 )
            {
                ADDRESS_MODE = AddressMode.MEM_64BIT_COMPRESSED_OOPS;
            }
            else
            {
                ADDRESS_MODE = AddressMode.UNKNOWN;
            }
        }
        catch( Exception e )
        {
            throw new Error( e );
        }
    }


    /** Return the size of the object excluding any referenced objects. */
    public static long shallowSizeOf( final Object object )
    {
        Class<?> objectClass = object.getClass();
        if( objectClass.isArray() )
        {
            // Array size is base offset + length * element size
            long size = UNSAFE.arrayBaseOffset( objectClass )
                    + UNSAFE.arrayIndexScale( objectClass ) * Array.getLength( object );
            return padSize( size );
        }
        else
        {
            // Object size is the largest field offset padded out to 8 bytes
            long size = OBJECT_BASE_SIZE;
            do
            {
                for( Field field : objectClass.getDeclaredFields() )
                {
                    if( (field.getModifiers() & Modifier.STATIC) == 0 )
                    {
                        long offset = UNSAFE.objectFieldOffset( field );
                        if( offset >= size )
                        {
                            size = offset + 1; // Field size is between 1 and PAD_SIZE bytes. Padding will round up to padding size.
                        }
                    }
                }
                objectClass = objectClass.getSuperclass();
            }
            while( objectClass != null );

            return padSize( size );
        }
    }


    private static final long padSize( final long size )
    {
        return (size + (OBJECT_ALIGNMENT - 1)) & ~(OBJECT_ALIGNMENT - 1);
    }


    /** Return the size of the object including any referenced objects. */
    public static long deepSizeOf( final Object object )
    {
        IdentityHashMap<Object,Object> visited = new IdentityHashMap<Object,Object>();
        Stack<Object> stack = new Stack<Object>();
        if( object != null ) stack.push( object );

        long size = 0;
        while( !stack.isEmpty() )
        {
            size += internalSizeOf( stack.pop(), stack, visited );
        }
        return size;
    }


    private static long internalSizeOf( final Object object, final Stack<Object> stack, final IdentityHashMap<Object,Object> visited )
    {
        // Scan for object references and add to stack
        Class<?> c = object.getClass();
        if( c.isArray() && !c.getComponentType().isPrimitive() )
        {
            // Add unseen array elements to stack
            for( int i = Array.getLength( object ) - 1; i >= 0; i-- )
            {
                Object val = Array.get( object, i );
                if( val != null && visited.put( val, val ) == null )
                {
                    stack.add( val );
                }
            }
        }
        else
        {
            // Add unseen object references to the stack
            for( ; c != null; c = c.getSuperclass() )
            {
                for( Field field : c.getDeclaredFields() )
                {
                    if( (field.getModifiers() & Modifier.STATIC) == 0 
                            && !field.getType().isPrimitive() )
                    {
                        field.setAccessible( true );
                        try
                        {
                            Object val = field.get( object );
                            if( val != null && visited.put( val, val ) == null )
                            {
                                stack.add( val );
                            }
                        }
                        catch( IllegalArgumentException e )
                        {
                            throw new RuntimeException( e );
                        }
                        catch( IllegalAccessException e )
                        {
                            throw new RuntimeException( e );
                        }
                    }
                }
            }
        }

        return shallowSizeOf( object );
    }
}

계측 등을 복잡하게 하지 않고 객체의 정확한 바이트 크기를 알 필요가 없다면 다음 접근 방식을 사용할 수 있습니다.

System.gc();
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();

do your job here

System.gc();
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();

이렇게 하면 사용된 메모리 전후를 읽고 사용된 메모리를 가져오기 직전에 GC를 호출하면 "노이즈"가 거의 0으로 낮아집니다.

보다 안정적인 결과를 얻으려면 작업을 n번 실행한 다음 사용된 메모리를 n으로 나누어 한 번 실행에 소요되는 메모리 양을 구할 수 있습니다.게다가 전체 작업을 더 많이 실행하여 평균을 낼 수도 있습니다.

당신이 요구하는 것이라면 메소드 호출이 없습니다.약간의 연구를 통해 직접 작성할 수 있을 것 같습니다.특정 인스턴스에는 참조 수와 기본 값 및 인스턴스 장부 데이터에서 파생된 고정 크기가 있습니다.단순히 객체 그래프를 따라가면 됩니다.행 유형의 다양성이 낮을수록 더 쉽습니다.

너무 느리거나 가치 있는 것보다 더 많은 문제가 있는 경우에는 항상 구식 행 계산 경험 법칙이 있습니다.

나는 즉석에서 추정하기 위해 한 번 빠른 테스트를 작성했습니다.

public class Test1 {

    // non-static nested
    class Nested { }

    // static nested
    static class StaticNested { }

    static long getFreeMemory () {
        // waits for free memory measurement to stabilize
        long init = Runtime.getRuntime().freeMemory(), init2;
        int count = 0;
        do {
            System.out.println("waiting..." + init);
            System.gc();
            try { Thread.sleep(250); } catch (Exception x) { }
            init2 = init;
            init = Runtime.getRuntime().freeMemory();
            if (init == init2) ++ count; else count = 0;
        } while (count < 5);
        System.out.println("ok..." + init);
        return init;
    }

    Test1 () throws InterruptedException {

        Object[] s = new Object[10000];
        Object[] n = new Object[10000];
        Object[] t = new Object[10000];

        long init = getFreeMemory();

        //for (int j = 0; j < 10000; ++ j)
        //    s[j] = new Separate();

        long afters = getFreeMemory();

        for (int j = 0; j < 10000; ++ j)
            n[j] = new Nested();

        long aftersn = getFreeMemory();

        for (int j = 0; j < 10000; ++ j)
            t[j] = new StaticNested();

        long aftersnt = getFreeMemory();

        System.out.println("separate:      " + -(afters - init) + " each=" + -(afters - init) / 10000);
        System.out.println("nested:        " + -(aftersn - afters) + " each=" + -(aftersn - afters) / 10000);
        System.out.println("static nested: " + -(aftersnt - aftersn) + " each=" + -(aftersnt - aftersn) / 10000);

    }

    public static void main (String[] args) throws InterruptedException {
        new Test1();
    }

}

일반적인 개념은 객체를 할당하고 여유 힙 공간의 변화를 측정하는 것입니다.핵심은 getFreeMemory(), 어느 GC 실행을 요청하고 보고된 여유 힙 크기가 안정화될 때까지 기다립니다..위의 출력은 다음과 같습니다.

nested:        160000 each=16
static nested: 160000 each=16

정렬 동작과 가능한 힙 블록 헤더 오버헤드를 고려하면 이것이 우리가 기대하는 것입니다.

여기에서 허용되는 답변에 자세히 설명된 계측 방법이 가장 정확합니다.내가 설명한 방법은 정확하지만 다른 스레드가 개체를 생성/삭제하지 않는 제어된 조건에서만 가능합니다.

Java Visual VM을 사용하십시오.

여기에는 메모리 문제를 프로파일링하고 디버깅하는 데 필요한 모든 것이 포함되어 있습니다.

또한 많은 유용한 작업을 수행할 수 있는 OQL(Object Query Language) 콘솔이 있으며 그 중 하나는 다음과 같습니다. sizeof(o)

내 대답은 Nick이 제공한 코드를 기반으로 합니다.해당 코드는 직렬화된 객체가 차지하는 총 바이트 양을 측정합니다.따라서 이것은 실제로 직렬화 항목 + 일반 객체 메모리 공간을 측정합니다(예를 들어 직렬화만 하면 됩니다). int 직렬화된 바이트의 총량이 다음과 같지 않다는 것을 알 수 있습니다. 4).따라서 개체에 정확히 사용되는 원시 바이트 번호를 얻으려면 해당 코드를 약간 수정해야 합니다.다음과 같습니다:

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class ObjectSizeCalculator {
    private Object getFirstObjectReference(Object o) {
        String objectType = o.getClass().getTypeName();

        if (objectType.substring(objectType.length()-2).equals("[]")) {
            try {
                if (objectType.equals("java.lang.Object[]"))
                    return ((Object[])o)[0];
                else if (objectType.equals("int[]"))
                    return ((int[])o)[0];
                else
                    throw new RuntimeException("Not Implemented !");
            } catch (IndexOutOfBoundsException e) {
                return null;
            }
        }

        return o;
    } 

    public int getObjectSizeInBytes(Object o) {
        final String STRING_JAVA_TYPE_NAME = "java.lang.String";

        if (o == null)
            return 0;

        String objectType = o.getClass().getTypeName();
        boolean isArray = objectType.substring(objectType.length()-2).equals("[]");

        Object objRef = getFirstObjectReference(o);
        if (objRef != null && !(objRef instanceof Serializable))
            throw new RuntimeException("Object must be serializable for measuring it's memory footprint using this method !");

        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(o);
            oos.close();
            byte[] bytes = baos.toByteArray();

            for (int i = bytes.length - 1, j = 0; i != 0; i--, j++) {
                if (objectType != STRING_JAVA_TYPE_NAME) {
                    if (bytes[i] == 112)
                        if (isArray)
                            return j - 4;
                        else
                            return j;
                } else {
                    if (bytes[i] == 0)
                        return j - 1;
                }
            }
        } catch (Exception e) {
            return -1;
        }

        return -1;
    }    

}

저는 기본 유형, 문자열 및 일부 사소한 클래스를 사용하여 이 솔루션을 테스트했습니다.또한 보장되지 않는 경우도 있을 수 있습니다.


업데이트: 배열 객체의 메모리 공간 계산을 지원하도록 수정된 예입니다.

힙 덤프(예: jmap 사용)를 생성한 다음 출력을 분석하여 개체 크기를 찾을 수 있습니다.이는 오프라인 솔루션이지만 얕은 크기와 깊은 크기 등을 검사할 수 있습니다.

long heapSizeBefore = Runtime.getRuntime().totalMemory();

// Code for object construction
...
long heapSizeAfter = Runtime.getRuntime().totalMemory();
long size = heapSizeAfter - heapSizeBefore;

크기는 객체 생성으로 인해 jvm의 메모리 사용량이 증가하며 일반적으로 객체의 크기입니다.

이 대답은 객체 크기와 관련이 없지만 객체를 수용하기 위해 배열을 사용하는 경우;객체에 할당할 메모리 크기입니다.

따라서 모든 컬렉션의 배열, 목록 또는 매핑은 실제로 개체를 저장하지 않고(기본 요소의 경우에만 실제 개체 메모리 크기가 필요함) 해당 개체에 대한 참조만 저장합니다.

이제 Used heap memory = sizeOfObj + sizeOfRef (* 4 bytes) in collection

  • (4/8바이트)는 (32/64비트) OS에 따라 다릅니다.

기초 요소

int   [] intArray    = new int   [1]; will require 4 bytes.
long  [] longArray   = new long  [1]; will require 8 bytes.

사물

Object[] objectArray = new Object[1]; will require 4 bytes. The object can be any user defined Object.
Long  [] longArray   = new Long  [1]; will require 4 bytes.

내 말은 모든 객체 REFERENCE에는 4바이트의 메모리만 필요하다는 뜻입니다.문자열 참조 또는 이중 개체 참조일 수 있지만 개체 생성에 따라 필요한 메모리가 달라집니다.

예) 아래 클래스에 대한 객체를 생성하는 경우 ReferenceMemoryTest 그러면 4 + 4 + 4 = 12바이트의 메모리가 생성됩니다.참조를 초기화하려고 할 때 메모리가 다를 수 있습니다.

 class ReferenceMemoryTest {
    public String refStr;
    public Object refObj;
    public Double refDoub; 
}

따라서 객체/참조 배열을 생성할 때 모든 내용은 NULL 참조로 채워집니다.그리고 우리는 각 참조에 4바이트가 필요하다는 것을 알고 있습니다.

마지막으로 아래 코드의 메모리 할당은 20바이트입니다.

ReferenceMemoryTest ref1 = 새로운 ReferenceMemoryTest();(4 (ref1) + 12 = 16 바이트) 참조 emeMoryTest ref2 = ref1;( 4(ref2) + 16 = 20바이트)

내가 이름이 붙은 클래스를 선언한다고 가정해보자. Complex 좋다:

public class Complex {

    private final long real;
    private final long imaginary;

    // omitted
}

이 클래스의 실제 인스턴스에 할당된 메모리 양을 확인하려면 다음을 수행하세요.

$ jmap -histo:live <pid> | grep Complex

 num     #instances         #bytes  class name (module)
-------------------------------------------------------
 327:             1             32  Complex

JSONObject의 경우 아래 코드가 도움이 될 수 있습니다.

`JSONObject.toString().getBytes("UTF-8").length`

크기를 바이트 단위로 반환합니다.

JSONArray 객체를 파일에 기록하여 확인했습니다.객체 크기를 제공합니다.

한 번만 수행하고 나중에 사용하기 위해 저장하려는 경우가 아니면 프로그래밍 방식으로 수행하고 싶지 않을 것입니다.비용이 많이 드는 일입니다.Java에는 sizeof() 연산자가 없으며, 존재하더라도 다른 객체에 대한 참조 비용과 기본 형식의 크기만 계산합니다.

이를 수행할 수 있는 한 가지 방법은 다음과 같이 항목을 파일로 직렬화하고 파일 크기를 확인하는 것입니다.

Serializable myObject;
ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("obj.ser"));
oos.write (myObject);
oos.close ();

물론 이는 각 개체가 고유하고 다른 개체에 대한 비일시적 참조를 포함하지 않는다고 가정합니다.

또 다른 전략은 각 개체를 가져와서 반영을 통해 해당 구성원을 검사하고 크기(부울 & 바이트 = 1바이트, 짧은 & 문자 = 2바이트 등)를 합산하여 구성원 계층 구조를 따라 내려가는 것입니다.그러나 이는 지루하고 비용이 많이 들고 결국 직렬화 전략과 동일한 작업을 수행하게 됩니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top