답변:
Mindprod 는 이것이 대답하기 쉬운 질문이 아니라고 지적합니다.
JVM은 임의의 패딩 또는 오버 헤드로 내부, 빅 또는 리틀 엔디안을 만족시키는 방식으로 데이터를 자유롭게 저장할 수 있지만 프리미티브는 공식적인 크기를 가진 것처럼 작동해야합니다.
예를 들어, JVM 또는 원시 컴파일러는 aboolean[]
와 같은 64 비트 길이의 청크 를 저장하기로 결정할 수 있습니다BitSet
. 프로그램이 동일한 답변을 제공하는 한 귀하에게 말할 필요는 없습니다.
- 스택에 일부 임시 객체를 할당 할 수 있습니다.
- 일부 변수 또는 메소드 호출을 완전히 존재하지 않고 최적화하여 상수로 대체 할 수 있습니다.
- 메소드 또는 루프의 버전을 지정할 수 있습니다. 즉, 각각 특정 상황에 최적화 된 두 가지 버전의 메소드를 컴파일 한 다음 호출 할 버전을 먼저 결정합니다.
물론 하드웨어와 OS에는 멀티 캐시, 온칩 캐시, SRAM 캐시, DRAM 캐시, 일반 RAM 작업 세트 및 디스크의 백업 저장소가 있습니다. 모든 캐시 수준에서 데이터가 복제 될 수 있습니다. 이 모든 복잡성으로 인해 RAM 소비를 매우 대략적으로 예측할 수 있습니다.
당신이 사용할 수있는 Instrumentation.getObjectSize()
객체가 소비 한 스토리지의 추정치를 얻는 데 .
실제 객체 레이아웃, 풋 프린트 및 참조 를 시각화 하기 위해 JOL (Java Object Layout) 도구를 사용할 수 있습니다. .
최신 64 비트 JDK에서 객체에는 12 바이트 헤더가 있으며 8 바이트의 배수로 채워져 있으므로 최소 객체 크기는 16 바이트입니다. 32 비트 JVM의 경우 오버 헤드는 8 바이트이며 4 바이트의 배수로 채워집니다. ( Dmitry Spikhalskiy의 답변 , Jayen의 답변 및 JavaWorld에서 에서)
일반적으로 참조는 32 비트 플랫폼 또는 64 비트 플랫폼에서 최대 4 바이트입니다 -Xmx32G
. 32Gb ( -Xmx32G
) 이상 8 바이트 ( 압축 된 객체 참조를 참조하십시오 .)
결과적으로 64 비트 JVM에는 일반적으로 30-50 % 더 많은 힙 공간이 필요합니다. ( 32 비트 또는 64 비트 JVM을 사용해야합니까? , 2012, JDK 1.7)
박스형 래퍼는 기본 유형 ( JavaWorld )에 비해 오버 헤드가 있습니다 .
Integer
: 16 바이트 결과는 예상 한 것보다 약간 나쁩니다.int
값이 4 바이트에 불과할 수 있기 때문 입니다. 사용Integer
오버 I는 기본 유형으로 값을 저장할 수있는 경우에 비해 좀 300 %의 메모리를 요한다
Long
: 16 바이트도 : 분명히 힙의 실제 객체 크기는 특정 CPU 유형에 대한 특정 JVM 구현에 의해 수행되는 낮은 수준의 메모리 정렬에 종속됩니다. aLong
는 8 바이트의 Object 오버 헤드와 실제 long 값의 경우 8 바이트가 더 있는 것 같습니다 . 대조적으로,Integer
사용하지 않는 JVM이 8 바이트 단어 경계에서 객체 정렬을 강제하기 때문에 사용되지 않는 4 바이트 구멍이 있습니다.
다른 용기들도 비용이 많이 든다 :
다차원 배열 : 또 다른 놀라움을 제공합니다.
개발자는 일반적으로 다음과 같은 구문을 사용합니다.int[dim1][dim2]
수치 및 과학 컴퓨팅과 합니다.에서
int[dim1][dim2]
배열 인스턴스, 모든 중첩 된int[dim2]
배열은이다Object
그 자체이다. 각각은 일반적인 16 바이트 배열 오버 헤드를 추가합니다. 삼각형 또는 거친 배열이 필요하지 않은 경우 순수한 오버 헤드를 나타냅니다. 배열 치수가 크게 다를 때 영향이 커집니다.예를 들어,
int[128][2]
인스턴스는 3,600 바이트를 사용합니다.int[256]
인스턴스가 사용 하는 1,040 바이트 (동일 용량)는 3,600 바이트가 246 %의 오버 헤드를 나타냅니다. 의 극단적 인 경우byte[256][1]
오버 헤드 계수는 거의 19입니다! 동일한 구문이 스토리지 오버 헤드를 추가하지 않는 C / C ++ 상황과 비교하십시오.
String
:String
의 메모리 증가는 내부 문자 배열의 증가를 추적합니다. 그러나String
클래스는 또 다른 24 바이트의 오버 헤드를 추가합니다.비어 있지 않은
String
크기가 10 자 이하인 경우 유용한 페이로드 (각 문자에 2 바이트 + 길이에 4 바이트)에 대한 추가 오버 헤드 비용의 범위는 100-400 %입니다.
이 예제 객체를 고려하십시오 .
class X { // 8 bytes for reference to the class definition
int a; // 4 bytes
byte b; // 1 byte
Integer c = new Integer(); // 4 bytes for a reference
}
순진 합계는 인스턴스가 X
17 바이트를 사용함을 나타냅니다. 그러나 정렬 (패딩이라고도 함)으로 인해 JVM은 8 바이트의 배수로 메모리를 할당하므로 17 바이트 대신 24 바이트를 할당합니다.
아키텍처 / jdk에 따라 다릅니다. 최신 JDK 및 64 비트 아키텍처의 경우 객체에는 12 바이트 헤더와 8 바이트 패딩이 있으므로 최소 객체 크기는 16 바이트입니다. Java Object Layout 이라는 도구 를 사용하여 크기를 결정하고 엔티티의 오브젝트 레이아웃 및 내부 구조에 대한 세부 사항을 얻거나 클래스 참조로이 정보를 추측 할 수 있습니다. 내 환경에서 정수의 출력 예 :
Running 64-bit HotSpot VM.
Using compressed oop with 3-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]
java.lang.Integer object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 12 (object header) N/A
12 4 int Integer.value N/A
Instance size: 16 bytes (estimated, the sample instance is not available)
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
따라서 Integer의 경우 인스턴스 크기는 16 바이트입니다. 헤더 바로 다음과 패딩 경계 바로 앞에 4 바이트 int가 압축되기 때문입니다.
코드 샘플 :
import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.util.VMSupport;
public static void main(String[] args) {
System.out.println(VMSupport.vmDetails());
System.out.println(ClassLayout.parseClass(Integer.class).toPrintable());
}
maven을 사용하는 경우 JOL을 얻으려면 다음을 수행하십시오.
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.3.2</version>
</dependency>
각 객체에는 관련 모니터 및 유형 정보와 필드 자체에 대한 특정 오버 헤드가 있습니다. 그 외에도 필드를 거의 배치 할 수는 있지만 JVM은 적합하다고 생각합니다 (그러나 믿는다) . 또 다른 대답에서 볼 수 있듯이 적어도 일부 JVM은 상당히 단단히 포장됩니다. 다음과 같은 수업을 고려하십시오.
public class SingleByte
{
private byte b;
}
vs
public class OneHundredBytes
{
private byte b00, b01, ..., b99;
}
32 비트 JVM에서 100 개의 인스턴스가 SingleByte
1200 바이트 (패딩 / 정렬로 인해 필드에 8 바이트의 오버 헤드 + 4 바이트)를 취할 것으로 예상 합니다. 하나의 인스턴스가 OneHundredBytes
108 바이트, 오버 헤드, 100 바이트를 차지할 것으로 예상 됩니다. JVM마다 확실히 다를 수 있습니다. 하나의 구현으로 필드를 압축하지 않기로 결정할 수 있습니다OneHundredBytes
408 바이트 (= 8 바이트 오버 헤드 + 4 * 100 정렬 / 패딩 바이트)가 필요합니다. 64 비트 JVM에서 오버 헤드도 더 클 수 있습니다 (확실하지 않음).
편집 : 아래의 의견을 참조하십시오; 분명히 핫스팟은 32 개가 아닌 8 바이트의 경계로 채워 지므로 각 인스턴스는 SingleByte
16 바이트를 차지합니다.
어느 쪽이든, "단일 큰 객체"는 간단한 작은 경우에 여러 개의 작은 객체만큼 효율적입니다.
프로그램의 총 사용 / 여유 메모리는 프로그램을 통해 얻을 수 있습니다.
java.lang.Runtime.getRuntime();
런타임에는 메모리와 관련된 몇 가지 방법이 있습니다. 다음 코딩 예제는 사용법을 보여줍니다.
package test;
import java.util.ArrayList;
import java.util.List;
public class PerformanceTest {
private static final long MEGABYTE = 1024L * 1024L;
public static long bytesToMegabytes(long bytes) {
return bytes / MEGABYTE;
}
public static void main(String[] args) {
// I assume you will know how to create a object Person yourself...
List < Person > list = new ArrayList < Person > ();
for (int i = 0; i <= 100000; i++) {
list.add(new Person("Jim", "Knopf"));
}
// Get the Java runtime
Runtime runtime = Runtime.getRuntime();
// Run the garbage collector
runtime.gc();
// Calculate the used memory
long memory = runtime.totalMemory() - runtime.freeMemory();
System.out.println("Used memory is bytes: " + memory);
System.out.println("Used memory is megabytes: " + bytesToMegabytes(memory));
}
}
모든 객체는 32 비트 시스템에서 16 바이트, 64 비트 시스템에서 24 바이트의 오버 헤드가있는 것으로 보입니다.
http://algs4.cs.princeton.edu/14analysis/ 는 좋은 정보원입니다. 많은 좋은 것들 중 하나의 예는 다음과 같습니다.
http://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-efficient-java-tutorial.pdf 는 다음과 같이 매우 유익합니다.
하나의 속성을 가진 100 개의 속성과 100 개의 속성을 가진 하나의 객체가 사용하는 메모리 공간이 각각 하나의 속성을 가지고 있습니까?
아니.
객체에 얼마나 많은 메모리가 할당됩니까?
속성을 추가 할 때 얼마나 많은 공간이 사용됩니까?
질문은 매우 광범위 할 것입니다.
클래스 변수에 따라 다르거 나 java에서 상태 메모리 사용으로 호출 할 수 있습니다.
또한 헤더 및 참조에 대한 추가 메모리 요구 사항이 있습니다.
Java 객체가 사용하는 힙 메모리에는 다음이 포함됩니다.
크기에 따른 프리미티브 필드의 메모리 (프리미티브 유형의 크기는 아래 참조);
참조 필드 용 메모리 (각 4 바이트);
몇 바이트의 "하우스 키핑"정보로 구성된 객체 헤더;
Java의 객체에는 객체의 클래스 기록, 객체에 도달 할 수 있는지 여부, 현재 동기화 잠금 상태 등의 상태 플래그와 같은 "하우스 키핑"정보가 필요합니다.
Java 객체 헤더 크기는 32 및 64 비트 jvm에 따라 다릅니다.
이것들이 주요 메모리 소비자이지만 jvm은 때로는 코드 정렬과 같은 추가 필드가 필요합니다.
기본 유형의 크기
부울 및 바이트 -1
char & short -2
int & float -4
롱 & 더블 -8
다른 답변에서 언급 한 java.lang.instrument.Instrumentation 접근법 에서 매우 좋은 결과를 얻었습니다 . 사용에 대한 좋은 예 는 JavaSpecialists 'Newsletter의 Instrumentation Memory Counter 항목 과 SourceForge 의 java.sizeOf 라이브러리 항목을 참조하십시오 .
누구에게나 유용한 경우 내 웹 사이트에서 객체의 메모리 사용량을 쿼리 하는 작은 Java 에이전트를 다운로드 할 수 있습니다 . "깊은"메모리 사용량도 쿼리 할 수 있습니다.
(String, Integer)
요소별로 Guava Cache가 사용 하는 메모리의 대략적인 추정치를 얻는 데 효과적이었습니다. 감사!
소비되는 메모리 양에 대한 규칙은 JVM 구현 및 CPU 아키텍처 (예 : 32 비트 대 64 비트)에 따라 다릅니다.
SUN JVM에 대한 자세한 규칙은 이전 블로그를 확인하십시오.
감사합니다, Markus