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

jvm实战-基本类型占多少内存的更多相关文章

  1. java对象占多少内存

    通常来说Hotspot jvm的对内存中的对象由以下几个部分组成 一个对象头,包含了一些整理工作所需信息 原始类型字段,不同类型大小各异(表1) 引用字段,占据4个字节(byte) 填充,在对象的末尾 ...

  2. 【JVM.2】垃圾收集器与内存分配策略

    垃圾收集器需要完成的3件事情: 哪些内存需要回收? 什么时候回收? 如何回收? 在前一节中介绍了java内存运行时区域的各个部分,其中程序计数器.虚拟机栈.本地方法栈3个区域随线程而生,随线程而灭:栈 ...

  3. 【转】深入JVM系列(一)之内存模型与内存分配

    http://lovnet.iteye.com/blog/1825324 一.JVM内存区域划分   大多数 JVM 将内存区域划分为 Method Area(Non-Heap),Heap,Progr ...

  4. JVM性能优化系列-(1) Java内存区域

    1. Java内存区域 1.1 运行时数据区 Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域.主要包括:程序计数器.虚拟机栈.本地方法栈.Java堆.方法区(运 ...

  5. JVM实战调优(空格引发的服务异常)

    JVM实战调优 问题描述 某一个项目中有一个文字转语音的服务,使用的是科大讯飞的语音转换服务,需要调用三方服务.因其转换服务是一个耗时操作,官方给的demo使用的是 WebSocket 进行数据转换操 ...

  6. JVM | 第1部分:自动内存管理与性能调优《深入理解 Java 虚拟机》

    目录 前言 1. 自动内存管理 1.1 JVM运行时数据区 1.2 Java 内存结构 1.3 HotSpot 虚拟机创建对象 1.4 HotSpot 虚拟机的对象内存布局 1.5 访问对象 2. 垃 ...

  7. JVM实用参数(四)内存调优

    理想的情况下,一个Java程序使用JVM的默认设置也可以运行得很好,所以一般来说,没有必要设置任何JVM参数.然而,由于一些性能问题(很不幸的是,这些问题经常出现),一些相关的JVM参数知识会是我们工 ...

  8. JVM学习笔记(四)------内存调优【转】

    转自:http://blog.csdn.net/cutesource/article/details/5907418 版权声明:本文为博主原创文章,未经博主允许不得转载. 首先需要注意的是在对JVM内 ...

  9. JVM学习笔记(四)------内存调优

    首先需要注意的是在对JVM内存调优的时候不能只看操作系统级别Java进程所占用的内存,这个数值不能准确的反应堆内存的真实占用情况,因为GC过后这个值是不会变化的,因此内存调优的时候要更多地使用JDK提 ...

随机推荐

  1. tomcat源代码Catalina

    Catalina的作用是初始化各个组件,并開始启动各个组件. 上文中介绍了Bootstrap是怎样启动Catalina的,如今来看看Catalina的作用: 1,Catalina通过Digester类 ...

  2. sql点滴42—mysql中的时间转换

    原文:sql点滴42-mysql中的时间转换 UNIX时间戳转换为日期用函数: FROM_UNIXTIME() select FROM_UNIXTIME(1156219870); 日期转换为UNIX时 ...

  3. Visual Studio 单元测试之五---数据库测试

    原文:Visual Studio 单元测试之五---数据库测试 数据库的单元测试主要是测试数据库中的数据是否符合特定的条件,Visual Studio 2010支持下面几种数据的单元测试类型(Visu ...

  4. SSI框架总结

    先来点文字性的描写叙述: MVC对于我们来说,已经不陌生了,它起源于20世纪80年代针对smalltalk语言的一种软件设计模式,如今已被广泛应用.近年来,随着java的盛行,MVC的低耦合性.高重用 ...

  5. [置顶] NB多项式事件模型、神经网络、SVM之函数/几何间隔——斯坦福ML公开课笔记6

    转载请注明:http://blog.csdn.net/xinzhangyanxiang/article/details/9722701 本篇笔记针对斯坦福ML公开课的第6个视频,主要内容包括朴素贝叶斯 ...

  6. ASP.NET MVC IOC之Unity攻略

    ASP.NET MVC IOC之Unity攻略 一.你知道IOC与DI吗? 1.IOC(Inversion of Control )——控制反转 即依赖对象不在被依赖模块的类中直接通过new来获取 先 ...

  7. Javascript技巧实例精选(3)—用字符在屏幕上打印金字塔

    用Javascript实现用★字符在屏幕上打印金字塔 >>点击这里下载完整html源码<< 这是最后的截图 这是相应的Javascript源码 //动态创建表格 var s=' ...

  8. C#函数式程序设计之泛型

    Intellij修改archetype Plugin配置 2014-03-16 09:26 by 破狼, 204 阅读, 0 评论,收藏, 编辑 Maven archetype plugin为我们提供 ...

  9. usaco 1.2.1(指针技巧)

    ★Milking Cows 挤牛奶 三个农民每天清晨 5 点起床,然后去牛棚给 3 头牛挤奶.第一个农民在 300 时刻(从 5 点开始计时,秒为单位)给他的牛挤奶,一直到 1000 时刻.第二个农民 ...

  10. 使用Reactive Extensions(Rx),对短时间内多次发生的事件限流

    使用Reactive Extensions(Rx),对短时间内多次发生的事件限流 牛刀小试:使用Reactive Extensions(Rx),对短时间内多次发生的事件限流 我之前有一篇文章介绍到了R ...