本文使用的是32位的JVM ,jdk1.6。本文基本是翻译的,加上了一些自己的理解,原文见文章底下链接。
    在本文中,我们讨论如何计算或者估计一个JAVA对象占多少内存空间。(注意,使用 Classmexer agent 或者VM insturmentation 可以查询到一个java对象占用了多少内存。)
    一般来说,我们讨论一个在堆中的对象的内存,前提是在“正常状态”下。我们忽略下面两种情况。
  • 在某些情况下,JVM 不一定会把对象放到堆中。例如,一个简单的线程本地对象可以存放在栈中。
  • 一个对象占用的内存还依赖于它的当前状态:例如,这个对象的同步锁是否是处于竞争状态,或者 这个对象是否正在被GC回收。
 
计算消耗内存的一般方法
 
一般来说,在Hotspot中,一个JAVA对象的内存消耗包括以下四个方面:
  1. 对象头,包括一些对象的状态。一个实例对象在堆中的内存消耗不仅仅花在它的属性上,这个对象还需要一些额外的信息,例如,保存一个对象的类引用和判断这个对象是否可以到达的,当前同步锁的情况的状态标记符等。

    在HotSpot中:(其他的JVM的情况也跟以下描述差不多。)

    • 一个普通的对象需要8个字节的额外内存空间
    • 一个数组需要12个字节(普通对象头的8个字节+4个)
  2. 基本类型的内存消耗,如int,long,float
  3. 引用属性的内存消耗,每个引用消耗4个字节。
  4. 填充,一个对象可能有一小部分的消耗浪费在填充上面。在HotSpot中,给对象分配内存最小单位是8个字节所以每个对象的占的字节数都是可以被8整除的。如果一个对象所占的字节数不是8的倍数,那么向上取最接近的可以被8整除的数字。
 
例如:
    一个空的实例对象占8个字节。
    一个包含一个 boolean  属性的对象占用16个字节内存:对象头占了8个,boolean 属性占了1个,8+1=9字节,因为9不是8的倍数,向上取最小可以被8整除的数字16。
    一个包含了8个 boolean 属性的对象也是占用16个字节内存:对象头8个,boolean 属性占了1*8个,一共16个,16可以被8整除,不需要填充字节。
    一个包含了两个long属性,3个int属性和一个boolean的对象占用了40个字节内存:
  • 对象头占用了8个字节
  • 2个long占用了16字节
  • 3个int占用了12个字节
  • 1个boolean占用了1个字节
  • 填充字节占用了3个字节。前面一共占用了37个字节,37不能被8整除,取40字节。
 
怎么去计算一个JAVA数组使用的内存
 
    前面写到了怎么计算一个JAVA对象的内存消耗,下面我们讨论下特殊情况数组。我们知道:
  • 在JAVA中,数组是一种特殊类型的对象
  • 一个多维数组是一个简单数组的数组
    例如,一个二维数组的每一行都是一个独立的数组对象
 
单个数组的内存使用情况
    一个一维数组是一个对象,数组也有对象头,可是,这个对象头占用了12个字节,额外的4个字节用来存储数组的长度。具体每个元素需要多少字节,取决于元素的类型。每一个元素需要4个字节来存储它的引用。并且如果总的字节数不是8个倍数,同样需要填充字节。
 
二维数组的占用内存情况
    在c语言中,二维数组(事实上是任何多维数组)本质上是一维数组通过指针操作来实现的。但是JAVA中并不是这样,JAVA中多维数组事实上是一系列的内嵌数组。这说明二维数组的每一行都有和对象一样的内存开销,因为他本质上就是一个独立的对象。
    例如,有一个10×10的int数组,首先,“外部”数组有12个字节的对象头开销。然后有十个元素,每个元素存储了一个指向10个元素数组的引用。以上的开销是12+4*10=52字节。然后每一行中都有一个数组对象,一个数组对象的对象头开销为12字节,并且有10个int.开销10*4 = 40 字节,另外还有4个填充字节,所以每一行的数组对象占用56个字节。所以一共有56*10+52 = 612个字节,加上4个填充字节一共616个字节。所以,得出的字节总数会比如果只是考虑10*10*4=400 字节要多。 多维数组和二维数组同理可得。
 
                                
 
 
JAVA String和 String 相关的对象的内存消耗
 
    我们的应用基本都会用到字符串,如果你用到C语言,并且使用ASCII编码,字符串由数字,字母或者某些特殊符号组成。那么每个字符串的大小就是字符数目*1字节。但是JAVA不是这样的:
    首先,每个对象都包含对象头,所以一个对象最低占用8个字节。
  • 一个JAVA String 包含不止一个对象
  • 一个JAVA char占用两个字节。
  • 一个JAVA对象包含一些额外的变量。
 
怎么计算字符串内存的使用情况
    首先一个占用最少字节的String,可以用以下公式计算。
8 * (int) ((((no chars) * 2) + 45) / 8)
    或者,用这个方法计算:   
  • 把字符串的字符个数*2个字节
  • 增加38
  • 如果结果不是8的倍数,取比结果大并且最接近的可以被8整除的数。
    这个结果就是String在堆中最小的占用内存字节数。
 
更进一步
    一般来说,上面的公式可以用于“新建”的String ,可是,还有另外的情况如下:
  • 如果一个String是另外一个String的子字符串,那么这个String会比上面说到的最小值要大。
  • 一个子字符串可以共用同一个字符数组,所以总体来说,一个父字符串加上几个子字符的消耗要比用上面公式计算的总和要小。
 
理解String 是怎么占用内存的
 
    来看一个每个String对象的各个属性,一个String包括如下的属性:
  • 一个char数组(是个独立的对象用来存储字符串中的字符)
  • 一个int 的offset属性(偏移量,用来指出字符串是从char数组中第几个字符开始的)
  • 一个int 的count属性(字符串的长度)
  • 最后一个int的hash属性(用来存储hashCode的值)
 
    也就是说,即使一个String不包含任何字符,也需要在数组的引用上面消耗4个字节,再加上3个int类型的属性,3*4=12字节,加上对象头的8个字节,以上一共24个字节(目前还不需要加上填充字节)。然后,一个char数组的对象头需要12个字节,加上4个填充字节,一个空的String 消耗了40字节。
    如果String 包含了17个字符,那么String 对象本身需要24个字节,但是现在17个字符的char数组要需12字节 加上 17*2=34字节,12+17*2=46字节,46不是8的倍数,加上填充字节46+2=48字节,那么17个字符的字符串会用到48+24 = 72 字节,可以看到在C语言中占据18个字节的String 在JAVA中占据了72个字节。
 
子字符串的内存消耗
 
        你可能会感到奇怪为什么字符串会有一个offset的属性和count属性。为什么char数组中的字符直接对应整个字符串呢?是这样的,当你创建一个子字符串,新创建的子字符串的char数组其实是指向的父字符串的char数组的,也就是父子字符串共享一个char数组。(但是他们有不同的offset和count)。这是一件好事还是坏事依赖于你怎么用:
  • 如果在创建完子字符串之后还需要用到父字符串的话,你可以省了些内存。
  • 如果创建完子字符串之后就不再需要用到父字符串的话,那么就浪费了内存

例如以下的代码
String str = "Some longish string...";
str.substring(5, 4);
    在这代码中str的char 数组不止是包括了4个字符,它包括了完整的字符串“Some longish string…”,但是str的offset,count属性改变了。如果这些内存的浪费在应用中是不可以接受的,那么我可以可以重新创建一个新的String :
 
String str = "Some longish string...";
str = new String(str.substring(5,4));
 
  用这种方式创建一个新的Stringchar数组就不会再直接指向"Some longish string…",而是会创建一个适合的4个字符的char 数组对象。
 

资料:

http://www.javamex.com/tutorials/memory/object_memory_usage.shtml 介绍JAVA对象的内存占用
http://www.javamex.com/tutorials/memory/array_memory_usage.shtml  介绍JAVA数组的内存占用
http://www.javamex.com/tutorials/memory/string_memory_usage.shtml 介绍JAVA字符串的内存占用

JAVA对象是如何占用内存的的更多相关文章

  1. java优化占用内存的方法(一)

    java做的系统给人的印象是什么?占 内存!说道这句话就会有N多人站出来为java辩护,并举出一堆的性能测试报告来证明这一点.其实从理论上来讲java做的系统并不比其他语言开发出来的 系统更占用内存, ...

  2. 关于Java占用内存的研究

    最近对程序占用内存方面做了一些优化,取得了不错的效果,总结了一些经验简要说一下,相信会对大家写出优质的程序有所帮助下面的论述针对32位系统,对64位系统不适用,后叙 经常你写了一个程序,一测试,功能没 ...

  3. 一步步优化JVM四:决定Java堆的大小以及内存占用

    到目前为止,还没有做明确的优化工作.只是做了初始化选择工作,比如说:JVM部署模型.JVM运行环境.收集哪些垃圾回收器的信息以及需要遵守垃圾回收原则.这一步将介绍如何评估应用需要的内存大小以及Java ...

  4. Java进程占用内存过高,排查解决方法

    最近收到邮件报警,说内存使作率达到84%.如下图: 解决方法: A:可能是代码原因导致的问题: 1.使用命令:top 查看当前进程的状态 2.从上图可以看到PID:916的java进程占用内存较大.定 ...

  5. java 一个对象多少大,占用多少内存

    1.instrumentation这种方法还是靠谱的 一个对象占用多少字节? 2.sizeof库 <!-- https://mvnrepository.com/artifact/com.carr ...

  6. 数据库数据在Java占用内存简单估算

    数据库数据在Java占用内存简单估算 结论: 1.数据库记录放在JAVA里,用对象(ORM一般的处理方式)须要4倍左右的内存空间.用HashMap这样的KV保存须要10倍空间; 2.假设你主要数据是t ...

  7. Java 进程占用内存过多,幕后元凶原来是线程太多

    那天中午吃饭,一个同事说,那个项目组的人快气死我了,程序有问题,早晨在群里@了他们,到中午才回消息,然后竟然还说他们的程序没有问题,是我们这边调用的太频繁了. 简直想笑. 背景说明 我们当前这个系统和 ...

  8. 对《java程序员上班那点事》笔者对数组占用内存质疑

    1.<java程序员上班那点事>笔者对数组占用内存的描述 2.实际测试情况: /** * 测试一维数组占用内存 */ public static void testOneArray() { ...

  9. Java中的CPU占用高和内存占用高的问题排查

    下面通过模拟实例分析排查Java应用程序CPU和内存占用过高的过程.如果是Java面试,这2个问题在面试过程中出现的概率很高,所以我打算在这里好好总结一下. 1.Java CPU过高的问题排查 举个例 ...

随机推荐

  1. mysql中 case when的使用

    SELECT a.hsid, a.house_code, a.sale_date, a.pjid, COUNT( sdid ) AS num, b.hsid, b.pscid, b.hscode, b ...

  2. Helpers\Hooks

    Helpers\Hooks Add modules with hooks The hooks helper allows modules to be created within the module ...

  3. vim中光标的前进和后退

    流行的文本编辑器通常都有前进和后退功能,可以在文件中曾经浏览过的位置之间来回移动.在 vim 中使用 Ctrl-O 执行后退,使用 Ctrl-I 执行前进.相关帮助: :help CTRL-O  :h ...

  4. 小白日记15:kali渗透测试之弱点扫描-漏扫三招、漏洞管理、CVE、CVSS、NVD

    发现漏洞 弱点发现方法: 1.基于端口服务扫描结果版本信息,比对其是否为最新版本,若不是则去其 官网查看其补丁列表,然后去逐个尝试,但是此法弊端很大,因为各种端口应用比较多,造成耗时大. 2.搜索已公 ...

  5. iOS开发之 动画CoreAnimation

    http://blog.treney.com/index.php/archives/CoreAnimation.html?hmsr=toutiao.io&utm_medium=toutiao. ...

  6. 使用JS制作一个鼠标可拖的DIV(一)——鼠标拖动

    使用 JS 来实现一个可拖动的DIV,主要是使用到以下几个事件: 1.鼠标按下:DIV元素的onmousedown. 2.鼠标按住拖动:document 的 onmousemove 元素. 3.鼠标放 ...

  7. [置顶] 《MFC游戏开发》笔记一 系列简介

    本系列文章由七十一雾央编写,转载请注明出处.  http://blog.csdn.net/u011371356/article/details/9299121 作者:七十一雾央 新浪微博:http:/ ...

  8. nmap命令-----基础用法

    系统漏洞扫描之王-nmap   NMap,也就是Network Mapper,是Linux下的网络扫描和嗅探工具包.   其基本功能有三个: (1)是扫描主机端口,嗅探所提供的网络服务 (2)是探测一 ...

  9. MyBatis(3.2.3) - Configuring MyBatis using XML, Mappers

    Mapper XML files contain the mapped SQL statements that will be executed by the application using st ...

  10. Lombok(1.14.8) - @SneakyThrows

    @SneakyThrows @SneakyThrows,声明异常. package com.huey.lombok; import java.io.UnsupportedEncodingExcepti ...