jvm實戰-基本類型占多少內存
jvm內存占用模型
對象的內存結構
對象頭 Header
包含兩部分數據Mark Word和Kclass:
Mark Word:存儲對象自身的運行時數據,如hashCode、GC分代年齡、鎖狀態標志、線程持有的鎖、偏向線程ID、偏向時間戳等。這部分數據的長度在32位和64的虛擬機(未開啟指針壓縮)中分別為4B和8B,官方稱之為”Mark Word”。
類型指針 Kclass:即對象指向它的類元數據的指針,虛擬機通過這個指針來確定這個對象是那個類的實例。
如果對象是一個Java數組,那再對象頭中還必須有一塊用于記錄數組長度的數據。
對象頭在32位系統上占用8B,64位系統上占16B。 無論是32位系統還是64位系統,對象都采用8字節對齊。Java在64位模式下開啟指針壓縮,比32位模式下,頭部會大4B(mark區域變位8B,kclass區域被壓縮為4B),如果沒有開啟指針壓縮,頭部會大8B(mark和kclass都是8B)
實例數據 Instance Data
存放字段數據。
對齊填充 Padding
對象的起始地址必須是8字節的整數倍(對象大小=8字節*整數),如果沒有對齊時,需要通過對齊填充來補全。
綜上,對象內存占用情況如下:
對象總內存 = 對象頭(Header(Mark Word+Kclass))+實例數據(Instance Data)+對齊填充(Padding)
32位虛擬機:header (8B)=Mark Word(4B)+kclass(4B)
64位沒有開啟指針壓縮:header (16B)=Mark Word(8B)+kclass(8B)
64位開啟指針壓縮:header (12B)=Mark Word(8B)+kclass(4B)
詳細了解jvm理論
基于maven的內存分析工具
項目結構

SizeOfAgent
package com.mobjia.agent;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashSet;
import java.util.Set;
/**
* 對象占用字節大小工具類
*
* @author tianmai.fh
* @date 2014-03-18 11:29
*/
public class SizeOfAgent {
static Instrumentation inst;
public static void premain(String args, Instrumentation instP) {
inst = instP;
}
/**
* 直接計算當前對象占用空間大小,包括當前類及超類的基本類型實例字段大小、<br></br>
* 引用類型實例字段引用大小、實例基本類型數組總占用空間、實例引用類型數組引用本身占用空間大小;<br></br>
* 但是不包括超類繼承下來的和當前類聲明的實例引用字段的對象本身的大小、實例引用數組引用的對象本身的大小 <br></br>
*
* @param obj
* @return
*/
public static long sizeOf(Object obj) {
return inst.getObjectSize(obj);
}
/**
* 遞歸計算當前對象占用空間總大小,包括當前類和超類的實例字段大小以及實例字段引用對象大小
*
* @param objP
* @return
* @throws IllegalAccessException
*/
public static long fullSizeOf(Object objP) throws IllegalAccessException {
Set<Object> visited = new HashSet<Object>();
Deque<Object> toBeQueue = new ArrayDeque<Object>();
toBeQueue.add(objP);
long size = 0L;
while (toBeQueue.size() > 0) {
Object obj = toBeQueue.poll();
//sizeOf的時候已經計基本類型和引用的長度,包括數組
size += skipObject(visited, obj) ? 0L : sizeOf(obj);
Class<?> tmpObjClass = obj.getClass();
if (tmpObjClass.isArray()) {
//[I , [F 基本類型名字長度是2
if (tmpObjClass.getName().length() > 2) {
for (int i = 0, len = Array.getLength(obj); i < len; i++) {
Object tmp = Array.get(obj, i);
if (tmp != null) {
//非基本類型需要深度遍歷其對象
toBeQueue.add(Array.get(obj, i));
}
}
}
} else {
while (tmpObjClass != null) {
Field[] fields = tmpObjClass.getDeclaredFields();
for (Field field : fields) {
if (Modifier.isStatic(field.getModifiers()) //靜態不計
|| field.getType().isPrimitive()) { //基本類型不重復計
continue;
}
field.setAccessible(true);
Object fieldValue = field.get(obj);
if (fieldValue == null) {
continue;
}
toBeQueue.add(fieldValue);
}
tmpObjClass = tmpObjClass.getSuperclass();
}
}
}
return size;
}
/**
* String.intern的對象不計;計算過的不計,也避免死循環
*
* @param visited
* @param obj
* @return
*/
static boolean skipObject(Set<Object> visited, Object obj) {
if (obj instanceof String && obj == ((String) obj).intern()) {
return true;
}
return visited.contains(obj);
}
}
AgentMain
package com.mobjia.agent;
import java.io.File;
import java.util.HashMap;
public class AgentMain {
/**
* -XX:+UseCompressedOops: mark/4 + metedata/8 + 4 = 16
* -XX:-UseCompressedOops: mark/8 + metedata/8 + 4 + padding/4 = 24
*/
static class A {
int a;
}
/**
* -XX:+UseCompressedOops: mark/4 + metedata/8 + 4 + 4 + padding/4 = 24
* -XX:-UseCompressedOops: mark/8 + metedata/8 + 4 + 4 = 24
*/
static class B {
int a;
int b;
}
/**
* -XX:+UseCompressedOops: mark/4 + metedata/8 + 4 + 4 + padding/4 = 24
* -XX:-UseCompressedOops: mark/8 + metedata/8 + 8 + 4 + padding/4 = 32
*/
static class B2 {
int b2a;
Integer b2b;
}
/**
* 不考慮對象頭:
* 4 + 4 + 4 * 3 + 3 * sizeOf(B)
*/
static class C extends A {
int ba;
B[] as = new B[3];
C() {
for (int i = 0; i < as.length; i++) {
as[i] = new B();
}
}
}
static class D extends B {
int da;
Integer[] di = new Integer[3];
}
/**
* 會算上A的實例字段
*/
static class E extends A {
int ea;
int eb;
}
public static void main(String[] args) throws IllegalAccessException {
primitiveType();
wrapperType();
}
private static void primitiveType(){
//邏輯型boolean
boolean boolean1 =true;
System.out.println("sizeOf(boolean)=" + SizeOfAgent.sizeOf(boolean1));
//文本型char
char char1 = 0;
System.out.println("sizeOf(char)=" + SizeOfAgent.sizeOf(char1));
//整數型(byte、short、int、long)
byte byte1 = 0;
System.out.println("sizeOf(byte)=" + SizeOfAgent.sizeOf(byte1));
short short1 = 0;
System.out.println("sizeOf(short)=" + SizeOfAgent.sizeOf(short1));
int int1 = 0;
System.out.println("sizeOf(int)=" + SizeOfAgent.sizeOf(int1));
long long1 = 0;
System.out.println("sizeOf(long)=" + SizeOfAgent.sizeOf(long1));
//浮點型(float、double)
float float1 = 0;
System.out.println("sizeOf(float)=" + SizeOfAgent.sizeOf(float1));
double double1 =1;
System.out.println("sizeOf(double)=" + SizeOfAgent.sizeOf(double1));
}
private static void wrapperType(){
//邏輯型boolean
java.lang.Boolean boolean1 =true;
System.out.println("sizeOf(java.lang.boolean)=" + SizeOfAgent.sizeOf(boolean1));
//文本型char
java.lang.Character char1 = 0;
System.out.println("sizeOf(java.lang.Character)=" + SizeOfAgent.sizeOf(char1));
//整數型(byte、short、int、long)
java.lang.Byte byte1 = 0;
System.out.println("sizeOf(java.lang.Byte)=" + SizeOfAgent.sizeOf(byte1));
java.lang.Short short1 = 0;
System.out.println("sizeOf(java.lang.Short)=" + SizeOfAgent.sizeOf(short1));
java.lang.Short int1 = 0;
System.out.println("sizeOf(java.lang.Short)=" + SizeOfAgent.sizeOf(int1));
java.lang.Long long1 = 0l;
System.out.println("sizeOf(java.lang.Long)=" + SizeOfAgent.sizeOf(long1));
//浮點型(float、double)
java.lang.Float float1 = 0f;
System.out.println("sizeOf(java.lang.Float)=" + SizeOfAgent.sizeOf(float1));
java.lang.Double double1 =1d;
System.out.println("sizeOf(java.lang.Double)=" + SizeOfAgent.sizeOf(double1));
}
}
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mobjia</groupId>
<artifactId>mobjia-jvm</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>mobjia-jvm</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<finalName>SizeOfAgent</finalName>
<archive>
<manifestEntries>
<Premain-class>com.mobjia.agent.SizeOfAgent</Premain-class>
<Boot-Class-Path></Boot-Class-Path>
<Can-Redefine-Classes>false</Can-Redefine-Classes>
</manifestEntries>
<addMavenDescriptor>false</addMavenDescriptor>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>1.2.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.mobjia.agent.AgentMain</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<defaultGoal>compile</defaultGoal>
</build>
</project>
java基本類型內存占用分析
以下是基于64位HotSpot虛擬機。
生成jar包
通過maven install 直接生成jar包,jar包在 項目\target文件夾下。


基本類型內存占用情況

開啟指針壓縮
通過vm參數 -XX:+UseCompressedOops 開啟指針壓縮

boolean
對象頭
header(12B)=Mark Word(8B)+kclass(4b)
實例數據
Instance Data (1B)
對齊填充
Padding = 2*8B -( header(12B)+(1B)) = 3B
所以boolean占用16B
int
對象頭
header(12B)=Mark Word(8B)+kclass(4b)
實例數據
Instance Data (4B)
對齊填充
Padding = 2*8B -( header(12B)+(4B)) = 0B
所以int占用16B
long
對象頭
header(12B)=Mark Word(8B)+kclass(4b)
實例數據
Instance Data (8B)
對齊填充
Padding = 3*8B -( header(12B)+(8B)) = 4B
所以long占用24B
關閉指針壓縮
通過vm參數 -XX:-UseCompressedOops 關閉指針壓縮

boolean
對象頭
header(16B)=Mark Word(8B)+kclass(8b)
實例數據
Instance Data (1B)
對齊填充
Padding = 3*8B -( header(16B)+(1B)) = 7B
所以boolean占用24B
int
對象頭
header(16B)=Mark Word(8B)+kclass(8b)
實例數據
Instance Data (4B)
對齊填充
Padding = 3*8B -( header(16B)+(4B)) = 4B
所以int占用24B
long
對象頭
header(16B)=Mark Word(8B)+kclass(8b)
實例數據
Instance Data (8B)
對齊填充
Padding = 3*8B -( header(16B)+(8B)) = 0B
所以long占用24B
來自:http://www.cnblogs.com/tenghoo/p/jvm_primitive_memory.html