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


617

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

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


에이전트에 mvn 구성을 추가하고 여기에 방법을 설명했습니다. stackoverflow.com/a/36102269/711855
juanmf

답변:


461

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

2
@Stefan 좋은 힌트! 당신의 크기입니다 무엇을 말해 주시겠습니까 byte[0], byte[1], byte[5], int[0], int[1], int[2]접근 방법을 사용하여 당신이 설명? 결과에 배열 길이 및 메모리 정렬에 대한 오버 헤드가 포함되어 있으면 좋을 것입니다.
dma_k

8
나는 이것을 시도하고 이상하고 도움이되지 않는 결과를 얻었다. 끈은 크기에 관계없이 항상 32입니다. 나는 이것이 아마도 포인터 크기라고 생각했지만 내가 만든 또 다른 불변 클래스에 대해서는 24를 얻었습니다. 프리미티브에는 잘 작동하지만 실제로 문자의 크기를 알려주는 프로그램이 필요하지 않습니다.
Brel

6
@Brel이 솔루션은 문서에 지정된대로 "지정된 개체가 소비 한 저장 공간의 근사치"일뿐입니다. 또한 저자는 Java의 String 풀로 인해 String의 크기를 32 바이트 (포인터 만?)로 설정하기로 결정했다고 가정합니다. 이는 String 인스턴스가 공유되는지 (풀에 저장되어 있는지) 또는 수업에 로컬 및 고유.
안드레이 I

11
jar을 내 보내지 않으면 어떻게 ObjectSizeFetcher를 사용자화할 수 있습니까? 일식에서 Java 프로젝트를 테스트했습니다.
Yura Shinkarev

3
@brel 실제 길이에 관계없이 문자열이 32 바이트에 불과한 이유는 문자열의 가변 길이 부분이 자체 객체 인 char []에 저장되기 때문입니다. 객체의 실제 크기를 얻으려면 자체 크기와 참조하는 각 객체의 크기를 추가해야합니다.
tombrown52

117

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 및 가비지 콜렉터 내부를 분석하는 방법을 보여줍니다.


18
이 답변에는 더 많은 투표가 있어야합니다. 확인하기에 아주 좋은 옵션입니다. 편집 : '08 년에 질문을하는 동안 올해 추가되었는지 확인했습니다. 아마도 OP가 요청한 것을 수행하는 가장 쉽고 쉬운 옵션 일 것입니다.
임대

4
도구 작성자는 Jol에 대한 블로그 게시물을 작성 했습니다 .
Mike

2
객체 "obj"의 크기를 결정하려면 다음을 사용하십시오 .org.openjdk.jol.info.GraphLayout.parseInstance (obj) .totalSize ();
활발한

참고 vmDetails지금이다 VM.current().details().
Miha_x64

GraphLayout.parseInstance(instance).toFootprint()객체 크기를 이해하는 것이 더 유용한 것으로 확인되었습니다
Mugen

82

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

3
여기서도 위에서 제안한 다른 솔루션을 시도하고 ObjectSizeCalculator를 발견했습니다. 나는 그것이 Nashorn 프로젝트의 일환으로 JDK 8에 최근 소개 된 이래로 아무도 언급하지 않았다고 믿는다 . 그러나 웹 에서이 수업에 대한 공식 문서를 찾지 못했습니다.
Henrique Gontijo

문자열 길이를 고려하지 않는 것 같습니다. 스택 크기 정도입니까?
jontejj

1
com.carrotsearch.RamUsageEstimator가 ObjectSizeCalculator의 약 절반을 반환하는 해시 맵이 있습니다. 어느 것이 사실입니까? -어느 쪽이 더 신뢰할 수 있습니까?
badera

9
ObjectSizeCalculator에만 핫스팟 VM에서 지원됩니다
kellanburket의

74

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

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

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

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


6
유틸리티의 크기가 아마도 가장 빠른 방법 일 것입니다. 기본적으로 스테판이 말했지만 이미 사용할 준비가 된 항아리에 담겨 있습니다.
Alexandre L은

62

먼저 "객체의 크기"는 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 크기를 적절한 값으로 초기화하면 코드가 조금 더 효율적입니다.


2
나는이 접근법을 좋아한다. 객체 크기 측면에서 얼마나 멀리 떨어져 있습니까?
Berlin Brown

1
매우 간단하고 효과적입니다. 다른 방법은 너무 지저분합니다 (특히 Eclipse RCP 내부). 감사.
marcolopes

19
직렬화는 임시 변수를 추적하지 않으며 기본 직렬화 방법은 문자열을 UTF-8로 작성하므로 모든 ANSI 문자는 1 바이트 만 사용합니다. 줄이 많으면 크기가 쓸모 없을 것입니다.
TMN

1
이것은 정확한 크기를 제공하지는 않지만 내 요구에 따라 2 개의 객체와 SizeOf 간의 비교 만 필요했습니다. 감사!
Isaac Isaac

1
YourKit의 좋은 추천 . 다른 대안은 VirtualVMjvmmonitor입니다.
angelcervera

38

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


24
가비지 수집이 언제 발생하는지 또는 한 번에 힙에 할당되는 추가 메모리 양을 알 수 없으므로 이는 좋은 해결책이 아닙니다.
Nick Fortescue

5
그것은 사실이며,이 게시물의 주요 질문을 해결하기 위해 이것을 의도하지는 않지만 최대 힙 크기에 도달 할 때 프로그래밍 방식으로 아는 데 도움이 될 수 있습니다.
matt b

1
이 솔루션의 다른 문제는 웹 서버와 같이 다중 스레드 환경에있을 때입니다. 다른 스레드가 실행 중이거나 메모리를 소비했을 수 있습니다. 이 근사값을 사용하면 모든 가상 머신에서 사용한 메모리를 계산합니다.
angelcervera

1
또 다른 단점은 freeMemory가 근사값을 반환한다는 것입니다. javax.crypto.Cipher 오브젝트를 작성하십시오. freeMemory에 대한 두 호출 간의 차이 (암호 크기 추정)는 일정하지 않습니다!
Eugen

1
가비지 콜렉션을 강제 실행할 수 있으므로이 방법으로 몇 가지 작업을 수행 할 수 있습니다 .
matanster

24

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

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


1
시아! 프레젠테이션 도 외치고 싶습니다 . 슬라이드 15-20은 다양한 데이터 구조 결정에 대한 본능적 인 느낌을 얻는 데 도움이됩니다. 게시 해 주셔서 감사합니다!
Luke Usherwood

16
"외부 의존성이 없다"-구아바는 언제 외부 의존성이 아닌가?
l4mpi


Guave는 외부 의존성입니다.
Mert Serimer

18

다른 답변의 대부분은 얕은 크기를 제공합니다. 예를 들어 키 또는 값이없는 HashMap의 크기는 원하는 것 같지 않습니다.

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

new MemoryMeter().measureDeep(myHashMap);

https://github.com/jbellis/jamm

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


11

반사를 사용하여 물체를 걸어야합니다. 다음과 같이 조심하십시오.

  • 객체를 할당하는 것만으로도 JVM에 약간의 오버 헤드가 있습니다. 금액은 JVM에 따라 다르므로이 값을 매개 변수로 만들 수 있습니다. 적어도 상수 (8 바이트?)로 만들고 할당 된 모든 것에 적용하십시오.
  • byte이론적으로 1 바이트 이기 때문에 메모리에 1 바이트 만 필요하다는 의미는 아닙니다.
  • 객체 참조에 루프가 있으므로 무한 루프를 제거하기 위해 객체 등호를 비교기로 사용HashMap 하거나 이와 같은 것을 유지해야합니다 .

@ jodonnell : 솔루션의 단순함을 좋아하지만 많은 객체를 직렬화 할 수 없으므로 (예외가 발생할 수 있음) 필드는 일시적 일 수 있으며 객체는 표준 메소드를 무시할 수 있습니다.


Java 사양에 정의 된 다양한 프리미티브의 크기가 아닙니까? (§2.4.1)
erickson

4
"메모리가 얼마나 많은 메모리를 차지하고 있는가"라는 의미가 아닙니다. 작동 방식의 관점에서만. 예를 들어, 바이트, 문자 및 반바지는 반올림 등으로 작동하더라도 Java 스택에서 전체 단어를 사용합니다.
Jason Cohen

1
이것은 뉴스 레터 # 78 ( javaspecialists.eu/archive/Issue078.html) 에 Heinz가 보여준 것처럼 크기 측정과 비슷하게 들립니다 . 나는 그것을 사용했다. 그의 접근 방식은 효과적입니다.
Peter Kofler

8

도구로 도구를 측정하거나 수동으로 추정해야하며 사용중인 JVM에 따라 다릅니다.

개체마다 고정 된 오버 헤드가 있습니다. JVM마다 다르지만 보통 40 바이트로 추정됩니다. 그런 다음 반원들을 살펴 봐야합니다. 객체 참조는 32 비트 (64 비트) JVM에서 4 바이트입니다. 기본 유형은 다음과 같습니다.

  • 부울 및 바이트 : 1 바이트
  • char 및 short : 2 바이트
  • int 및 float : 4 바이트
  • long 및 double : 8 바이트

배열은 동일한 규칙을 따릅니다. 즉, 객체 참조이므로 객체에 4 (또는 8) 바이트가 걸리고 길이에 요소 크기가 곱해집니다.

Runtime.freeMemory()가비지 수집기 등의 비동기 호출로 인해 호출 방식으로 프로그래밍 방식으로 시도 하면 정확도가 크게 향상되지 않습니다. -Xrunhprof 또는 기타 도구를 사용하여 힙을 프로파일 링하면 가장 정확한 결과를 얻을 수 있습니다.


@erickson 나는이 스레드를 보는 sizeof (boolean) == 1에 대해 확신하지 못합니다 ( stackoverflow.com/questions/1907318/… ). 이것에 대해 의견을 말씀해 주시겠습니까?
dma_k

2
@ dma_k, Java에는 실제로 실제 부울이 없습니다. 부울의 크기는 배열 외부에서 4 바이트이고 내부에서 1 바이트입니다 boolean[]. 실제로 더블 / 롱 타입이 아닌 모든 프리미티브는 4 바이트입니다. 후자는 8입니다 (답변은 4로 잘못 설정합니다)
bestsss

@bestsss : 더 정확하게 말하면 최소 메모리 할당은 플랫폼 및 JVM 구현에 따라 다릅니다. 또한 힙의 객체가 정렬되므로 모든 크기를 합한 후에 반올림해야합니다.
dma_k

6

java.lang.instrument.Instrumentation클래스는 Java 객체의 크기를 얻는 좋은 방법을 제공하지만 premainJava 에이전트를 사용하여 프로그램 을 정의 하고 실행해야합니다. 에이전트가 필요하지 않은 경우 매우 지루하며 애플리케이션에 더미 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() { }
}

흥미로운 접근 방법이지만 객체와 필드 스토리지가 조각화되지 않았다고 가정합니까?
nicoulaj

예, 그런 조각화를 만드는 JVM 구현을 모르겠습니다.
Miguel Gamboa

이해가 안 돼요 조각화는 옵션이 아닙니다 :) 객체 A와 B의 필드로 저장된 객체 C의 예를 들어 봅시다. A 또는 B에서 모든 것을 이동시키지 않습니까?
nicoulaj 2016 년

죄송하지만 귀하의 관점을 이해하지 못했습니다. 내 해석에 따르면 Java 객체에서는 C 구조 또는 .Net의 값 유형과 같이 다른 객체 안에 저장할 수 없습니다. "객체 A와 B의 필드로 저장된 객체 C"는 객체 A와 B에 객체 C에 대한 참조 (포인터)를 저장하는 필드가 있음을 의미합니다. 그러면 A와 B의 크기는 그 필드의 오프셋과 객체 C에 대한 참조 (포인터)의 크기를 더한 것
Miguel Gamboa 2016 년

오, 우리는 얕은 크기에 대해 이야기하고 있습니다. 내 잘못이야.
nicoulaj 2016 년

6

도있다 메모리 측정기의 (이전에 도구 Google 코드 지금, GitHub의 간단하고 상업 친화적 인 출판이다,) 아파치 2.0 라이선스 (A)에 설명 된대로 비슷한 질문은 .

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


4

계측 등을 엉망으로 만들지 않고 객체의 바이트 크기를 알 필요가 없으면 다음과 같은 접근 방식을 사용할 수 있습니다.

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

do your job here

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

이 방법을 사용하면 전후에 사용 된 메모리를 읽고 사용 된 메모리를 얻기 직전에 GC를 호출하여 "노이즈"를 거의 0으로 낮 춥니 다.

보다 안정적인 결과를 얻으려면 작업을 n 번 실행 한 다음 사용 된 메모리를 n으로 나누면 한 번의 실행에 필요한 메모리 양을 얻을 수 있습니다. 훨씬 더, 당신은 전체를 더 많은 시간을 실행하고 평균을 만들 수 있습니다.


5
하지 않습니다 System.gc()당신이 GC에 원하는 통지? GC가 전혀 호출되지는 않습니다.
Raildex

@reallynice. GC가 행하거나 행 사이의 메모리에 영향을 미치지 않을 수 있으므로 안전하지 않습니다. 따라서 두 개의 freeMemory 메소드 사이에 "GC"는 고려하지 않은 더 많은 공간을 확보하여 오브젝트가 더 작게 보입니다.
Mert Serimer

@MertSerimer "안전하지 않음"은 나에게 완전히 다른 수준에 있습니다. 최대한 이것은 내가 언급 한 것처럼 그렇게 정확하지는 않습니다. 또한 RailC가 언급 한대로 GC를 구동 할 수 없지만이 경우에도 사이클에 삽입하는 것이 좋습니다. 명시된 바와 같이 결과가 매우 신뢰할 필요가없는 경우 작동하는 빠르고 더럽고 근사한 시스템입니다.
reallynice

이것에는 많은 문제가 있지만 좋은 스왑을 제공합니다.
markthegrea

3

다음은 압축 된 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 );
    }
}

이 클래스를 값으로 테스트 했습니까? 시도했지만 잘못된 값 !!!.
Débora

1
간단한 객체에 대해 나에게 준 값은 정확했지만 1mio 객체가 포함 된 목록의 경우 10 배가되었습니다. 아직도, 아주 좋은 일입니다!
Michael Böckling

흥미 롭군 Windows 7 x64 및 Linux 2.6.16 / x86_64에서 JDK7u67을 사용하여 각 32 비트 / 64 비트 / 루프 주소 모드를 사용하여 테스트했습니다. Eclipse Memory Analyzer 1.3.x에서 분석 한 메모리 덤프와 비교했습니다. 어떤 설정을 사용하고 있습니까? 내가 시도 할 수있는 구체적인 예가 있습니까?
dlaudams

내가 할 수있는 최선의 선택. VM 유형 (HotSpot)과 bacouse 스프링 빈을 잘 모르기 때문에 Instrumentation바람둥이를 시작하지 않기 때문에 사용할 수 없습니다 . 나는 이것을 사용 하고 클래스와 Enum을 무시하기 위해 싱글 톤 viz 와 리 팩터 코드를 무시하기 위해 두 번째 매개 변수를 추가합니다ObjectSizeCalculatorJOLAbstractRefreshableApplicationContext.getBeanFactory().getSingletonMutex()internalSizeOf
Perlos

결과를 비교하려면 ObjectSizeCalculator를 사용하십시오 (전체 서버 1GB-10 초 계산). JOL은 MemError (6GB는 포함되지 않음)를 유발하며 열거 형 때문에 동일한 결과를 얻지 못합니다.
Perlos

3

다음 요구 사항을 충족하는 객체 크기의 런타임 계산을 찾고있었습니다.

  • 계측을 포함하지 않아도 런타임에 사용 가능합니다.
  • Unsafe에 액세스하지 않고 Java 9 이상에서 작동합니다.
  • 클래스만을 기반으로합니다. 문자열 길이, 배열 길이 등을 고려하는 깊은 크기는 아닙니다.

다음은 원래 Java 전문가 기사 ( https://www.javaspecialists.eu/archive/Issue078.html ) 의 핵심 코드 와이 질문에 대한 다른 답변의 안전하지 않은 버전의 몇 가지 비트를 기반으로합니다.

누군가가 유용하다고 생각합니다.

public class JavaSize {

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 HEADER_SIZE = 8;

public static int sizeOf(Class<?> clazz) {
    int result = 0;

    while (clazz != null) {
        Field[] fields = clazz.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            if (!Modifier.isStatic(fields[i].getModifiers())) {
                if (fields[i].getType().isPrimitive()) {
                    Class<?> primitiveClass = fields[i].getType();
                    if (primitiveClass == boolean.class || primitiveClass == byte.class) {
                        result += 1;
                    } else if (primitiveClass == short.class) {
                        result += 2;
                    } else if (primitiveClass == int.class || primitiveClass == float.class) {
                        result += 4;
                    } else if (primitiveClass == double.class || primitiveClass == long.class) {
                        result += 8;
                    }

                } else {
                    // assume compressed references.
                    result += 4;
                }
            }
        }

        clazz = clazz.getSuperclass();

        // round up to the nearest WORD length.
        if ((result % WORD) != 0) {
            result += WORD - (result % WORD);
        }
    }

    result += HEADER_SIZE;

    return result;
}

}


2

그것이 요구하는 경우 메소드 호출이 없습니다. 약간의 연구만으로도 당신이 직접 쓸 수 있다고 생각합니다. 특정 인스턴스에는 참조 수와 프리미티브 값에 인스턴스 부기 데이터를 더한 크기가 고정되어 있습니다. 당신은 단순히 객체 그래프를 걷는 것입니다. 행 유형이 다양하지 않을수록 쉬워집니다.

그것이 가치보다 너무 느리거나 더 많은 문제가 있다면, 항상 규칙을 세는 구식 행이 좋습니다.


2

나는 즉시 평가하기 위해 빠른 테스트를 한 번 작성했습니다.

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();
    }

}

일반적인 개념은 사용 가능한 힙 공간에서 객체를 할당하고 변경을 측정하는 것입니다. 키는 GC 실행getFreeMemory()요청하고보고 된 사용 가능한 힙 크기가 안정화 될 때까지 대기합니다. 입니다. 위의 결과는 다음과 같습니다.

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

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

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


2

Java Visual VM을 사용하십시오.

메모리 문제를 프로파일 링하고 디버그하는 데 필요한 모든 것이 있습니다.

또한 많은 유용한 작업을 수행 할 수있는 OQL (Object Query Language) 콘솔이 있습니다. sizeof(o)


2

JetBrains IntelliJ를 사용하는 경우 먼저 File | 설정 | 빌드, 실행, 배포 | 디버거.

디버깅 할 때 관심있는 변수를 마우스 오른쪽 단추로 클릭하고 "보존 크기 계산"을 선택하십시오. 보유 크기 계산


1

내 대답은 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;
    }    

}

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


업데이트 : 배열 객체의 메모리 풋 프린트 계산을 지원하도록 예제가 수정되었습니다.


0

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


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

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

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


// 객체 생성을위한 코드 중 GC가 중간에 실행되면 어떻게 되나요? 이제 항상 올바른 결과를 얻을 수 있습니다.
rajugaadu

0

이 답변은 객체 크기와 관련이 없지만 객체를 수용하기 위해 배열을 사용할 때; 객체에 할당 할 메모리 크기

따라서 모든 컬렉션의 배열, 나열 또는 매핑은 객체를 실제로 저장하지 않을 것입니다 (원시 시점에서만 실제 객체 메모리 크기가 필요함). 그 객체에 대한 참조 만 저장합니다.

이제 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 바이트의 메모리 만 필요하다는 것을 의미합니다. 문자열 참조 또는 이중 개체 참조 일 수 있지만 개체 생성에 따라 필요한 메모리가 달라집니다.

예) 아래 클래스의 객체를 만들면 ReferenceMemoryTest4 + 4 + 4 = 12 바이트의 메모리가 생성됩니다. 참조를 초기화하려고 할 때 메모리가 다를 수 있습니다.

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

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

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

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


1
4 바이트 정수와 알 수없는 크기의 객체 참조는 어떻게 4 바이트에 적합합니까?
Lorne의 후작

@ EJP 나는 모든 객체 참조가 단지 4 바이트의 메모리를 필요로한다는 것을 의미합니다. 문자열 참조 또는 이중 개체 참조 일 수 있지만 개체 생성에 따라 필요한 메모리가 달라집니다.
Kanagavelu Sugumar

0

다음 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

-5

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

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

바이트 단위로 사이즈를 돌려줍니다

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


이것은 대부분 문자열 인 객체에 대해서만 작동합니다.
Dexter Legaspi

-6

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

당신이 할 수있는 한 가지 방법은 파일을 직렬화하고 다음과 같이 파일의 크기를 보는 것입니다.

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

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

또 다른 전략은 각 개체를 가져 와서 반영하여 구성원을 검사하고 구성원 계층 구조를 내려 가면서 크기 (부울 및 바이트 = 1 바이트, 짧은 및 char = 2 바이트 등)를 추가하는 것입니다. 그러나 그것은 지루하고 비싸며 직렬화 전략과 같은 일을합니다.


3
ByteArrayOutputStream을 사용하여 byte []로 직렬화합니다. 파일에 쓰는 것보다 훨씬 빠릅니다.
ScArcher2

@KorayTugay 객체의 바이트 크기를 결정하는 것은 이미 비용이 많이 드는 작업입니다. 크기를 결정하기 위해 각 객체를 디스크에 쓰는 것은 크롤링을 할 것입니다 ...
HammerNL

1
직렬화 된 객체 형식은 힙 메모리의 객체 형식과 완전히 다릅니다. 가장 주목할만한 것은 객체의 클래스 (및 모든 직렬화 가능한 슈퍼 클래스)에 대한 디스크립터가 스트림에 기록된다는 것입니다. 따라서 간단한 인스턴스를 작성 java.lang.Integer하면 힙 표현이 일반적으로 32 인 약 80 바이트 가 생성됩니다 (오브젝트 스트림 표현과 달리 힙 표현은 포인터 크기 및 오브젝트 정렬에 따라 다릅니다). 반대로, 직렬화 된 null참조는 힙 메모리에서 4 또는 8 바이트 대신 1 바이트를 필요로합니다.
Holger
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.