jvm實戰-基本類型占多少內存

tt89218 9年前發布 | 19K 次閱讀 JVM Java開發

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理論

jvm理論

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

 

 本文由用戶 tt89218 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!