Java에서 객체의 크기를 결정하는 가장 좋은 방법은 무엇입니까?
데이터 행 더미가있는 CSV 파일을 읽는 응용 프로그램이 있습니다. 사용자에게 데이터 유형에 따라 행 수에 대한 요약을 제공하지만 너무 많은 데이터 행을 읽고 OutOfMemoryError
s를 발생시키지 않도록하고 싶습니다 . 각 행은 객체로 변환됩니다. 프로그래밍 방식으로 해당 개체의 크기를 쉽게 찾을 수있는 방법이 있습니까? 에 대한 기본 유형 및 객체 참조의 크기를 정의하는 참조가 VM
있습니까?
지금은 read up to 32,000 rows 라는 코드가 있지만 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)
"(다른 것)"항목 은이 개체 그래프의 일부가 아닌 힙의 다른 개체를 설명합니다 .
최고의 jol 문서는 jol 저장소 의 jol 샘플 입니다. 샘플은 일반적인 jol 작업을 보여주고 jol을 사용하여 VM 및 가비지 수집기 내부를 분석하는 방법을 보여줍니다.
몇 년 전에 Javaworld는 복합 및 잠재적으로 중첩 된 Java 객체의 크기를 결정하는 기사를 가지고 있었으며 기본적으로 Java 에서 sizeof () 구현을 만드는 과정을 안내합니다. 이 접근 방식은 기본적으로 사람들이 실험적으로 프리미티브와 일반적인 Java 객체의 크기를 식별 한 다음 그 지식을 객체 그래프를 반복적으로 걷는 방법에 적용하여 전체 크기를 집계하는 다른 작업을 기반으로합니다.
클래스의이면에서 일어나는 일 때문에 항상 네이티브 C 구현보다 정확도가 다소 떨어질 수 있지만 좋은 지표가되어야합니다.
또는 sizeof () 구현과 함께 Java5 라이브러리를 제공하는 sizeof 라고하는 SourceForge 프로젝트가 적절 합니다.
추신 직렬화 접근 방식을 사용하지 마십시오. 직렬화 된 개체의 크기와 라이브 상태에서 사용하는 메모리 양간에 상관 관계가 없습니다.
실수로 jdk에있는 Java 클래스 "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
첫째, "객체의 크기"는 Java에서 잘 정의 된 개념이 아닙니다. 멤버, Object 및 참조하는 모든 개체 (참조 그래프) 만있는 개체 자체를 의미 할 수 있습니다. 메모리의 크기 또는 디스크의 크기를 의미 할 수 있습니다. 그리고 JVM은 문자열과 같은 것을 최적화 할 수 있습니다.
따라서 유일하게 올바른 방법은 좋은 프로파일 러 (저는 YourKit 사용 )를 사용하여 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 비트, 압축 된 oops, 64 비트), 패딩, 하위 클래스 패딩을 고려하여 원형 데이터 구조 및 배열에서 올바르게 작동합니다. 이 하나의 .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 바이트는 메모리에 하나 걸리는 것은 아닙니다. - 객체 참조에는 루프가 있으므로 무한 루프를 제거 하려면 객체 같음을 비교기로 사용하여 a
HashMap
또는 일부 를 유지해야합니다 .
@jodonnell : 솔루션의 단순성이 마음에 들지만 많은 개체가 직렬화 가능하지 않고 (예외가 발생 함) 필드가 일시적 일 수 있으며 개체가 표준 메서드를 재정의 할 수 있습니다.
도구로 측정하거나 손으로 추정해야하며 사용중인 JVM에 따라 다릅니다.
개체 당 고정 된 오버 헤드가 있습니다. JVM에 따라 다르지만 일반적으로 40 바이트를 추정합니다. 그런 다음 학급의 구성원을 봐야합니다. 개체 참조는 32 비트 (64 비트) JVM에서 4 (8) 바이트입니다. 기본 유형은 다음과 같습니다.
- 부울 및 바이트 : 1 바이트
- char 및 short : 2 바이트
- int 및 float : 4 바이트
- long 및 double : 8 바이트
배열은 동일한 규칙을 따릅니다. 즉, 객체 참조이므로 객체에서 4 (또는 8) 바이트를 취한 다음 길이에 해당 요소의 크기를 곱합니다.
Runtime.freeMemory()
가비지 수집기에 대한 비동기 호출 등으로 인해에 대한 호출로 프로그래밍 방식으로 수행하려고 시도 하면 정확도가 크게 향상되지 않습니다. -Xrunhprof 또는 기타 도구를 사용하여 힙을 프로파일 링하면 가장 정확한 결과를 얻을 수 있습니다.
이 java.lang.instrument.Instrumentation
클래스는 Java Object의 크기를 가져 오는 좋은 방법을 제공하지만 premain
Java 에이전트를 사용하여 프로그램 을 정의 하고 실행해야합니다. 에이전트가 필요하지 않고 애플리케이션에 더미 Jar 에이전트를 제공해야 할 때 이것은 매우 지루합니다.
그래서 사용하여 대체 솔루션 얻었다 Unsafe
으로부터 클래스를 sun.misc
. 따라서 프로세서 아키텍처에 따른 객체 힙 정렬을 고려하고 최대 필드 오프셋을 계산하면 Java Object의 크기를 측정 할 수 있습니다. 아래 예제에서는 보조 클래스 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() { }
}
도있다 메모리 측정기의 (이전에 도구 Google 코드 지금, GitHub의 간단하고 상업 친화적 인 출판이다,) 아파치 2.0 라이선스 (A)에 설명 된대로 비슷한 질문은 .
또한 메모리 바이트 소비를 측정하려면 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();
}
}
일반적인 개념은 개체를 할당하고 사용 가능한 힙 공간의 변화를 측정하는 것입니다. 키는 GC 실행getFreeMemory()
을 요청하고보고 된 여유 힙 크기가 안정화 될 때까지 기다립니다 . 위의 출력은 다음과 같습니다.
nested: 160000 each=16
static nested: 160000 each=16
정렬 동작과 가능한 힙 블록 헤더 오버 헤드를 고려할 때 이것이 우리가 기대하는 것입니다.
여기에서 허용되는 답변에 자세히 설명 된 계측 방법이 가장 정확합니다. 내가 설명한 방법은 정확하지만 다른 스레드가 객체를 생성 / 삭제하지 않는 제어 된 조건에서만 가능합니다.
Java Visual VM을 사용하십시오.
메모리 문제를 프로파일 링하고 디버깅하는 데 필요한 모든 것이 있습니다.
또한 많은 유용한 작업을 수행 할 수있는 OQL (Object Query Language) 콘솔이 있습니다. sizeof(o)
다음 요구 사항을 충족하는 개체 크기의 런타임 계산을 찾고있었습니다.
- 계측을 포함 할 필요없이 런타임에 사용할 수 있습니다.
- Unsafe에 대한 액세스없이 Java 9 이상에서 작동합니다.
- 클래스만을 기반으로합니다. 문자열 길이, 배열 길이 등을 고려하는 깊은 sizeOf가 아닙니다.
다음은 원본 자바 전문가 기사 ( 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;
}
}
내 대답은 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;
size는 객체 생성으로 인한 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 바이트) ReferenceMemoryTest 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 바이트, short & char = 2 바이트 등)를 더하여 멤버십 계층 구조 아래로 작업하는 것입니다. 그러나 이는 지루하고 비용이 많이 들고 결국 직렬화 전략이 수행하는 것과 동일한 작업을 수행하게됩니다.
'IT박스' 카테고리의 다른 글
cURL을 사용하여 요청 및 응답 시간을 한 번에 측정하려면 어떻게합니까? (0) | 2020.10.03 |
---|---|
파일의 줄 순서를 바꾸려면 어떻게해야합니까? (0) | 2020.10.03 |
"u"및 "r"문자열 플래그는 정확히 무엇을하며 원시 문자열 리터럴은 무엇입니까? (0) | 2020.10.03 |
다양한 커밋을 선택하고 다른 브랜치에 병합하는 방법은 무엇입니까? (0) | 2020.10.03 |
클래스의 항목 순서 : 필드, 속성, 생성자, 메서드 (0) | 2020.10.03 |