java.lang.instrument: 一个Java对象占用多少字节?
一、对象头包括两部分信息:Mark Word(标记字段)和 Klass Pointer(类型指针)
1. Mark Word 用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程 ID、偏向时间戳等等。
JVM 对象头一般占用两个机器码,在 32-bit JVM 上占用 64bit, 在 64-bit JVM 上占用 128bit 即 16 bytes(暂不考虑开启压缩指针的场景)。
另外,如果对象是一个 Java 数组,那在对象头中还必须有一块用于记录数组长度的数据,因为虚拟机可以通过普通 Java 对象的元数据信息确定 Java 对象的大小,但是从数组的元数据中无法确定数组的大小。
对象需要存储的运行时数据很多,其实已经超出了32、64位 Bitmap 结构所能记录的限度,但是对象头信息是与对象自身定义的数据无关的额外存储成本,考虑到虚拟机的空间效率,Mark Word 被设计成一个非固定的数据结构以便在极小的空间内存储尽量多的信息,它会根据对象的状态复用自己的存储空间。
例如在 32 位的HotSpot 虚拟机中对象未被锁定的状态下,Mark Word 的 32个Bits 空间中的 25Bits 用于存储对象哈希码(HashCode),4Bits 用于存储对象分代年龄,2Bits 用于存储锁标志位,1Bit固定为0,在其他状态(轻量级锁定、重量级锁定、GC标记、可偏向)下对象的存储内容如下表所示。
存储内容 |
标志位 |
状态 |
对象哈希码、对象分代年龄 |
01 |
未锁定 |
指向锁记录的指针 |
00 |
轻量级锁定 |
指向重量级锁的指针 |
10 |
膨胀(重量级锁定) |
空,不需要记录信息 |
11 |
GC标记 |
偏向线程ID、偏向时间戳、对象分代年龄 |
01 |
可偏向 |
- Klass Pointer,即是对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
二、UseCompressOops
通常64位JVM消耗的内存会比32位的大1.5倍,这是因为对象指针在64位架构下,长度会翻倍(更宽的寻址)。
对于那些将要从32位平台移植到64位的应用来说,平白无辜多了1/2的内存占用,这是开发者不愿意看到的。
幸运的是,从JDK 1.6 update14开始,64 bit JVM正式支持了 -XX:+UseCompressedOops 这个可以压缩指针,起到节约内存占用的新参数。
1. 什么是 OOP ?
OOP = “ordinary object pointer” 普通对象指针。
启用CompressOops后,会压缩的对象:
• 每个Class的属性指针(静态成员变量)
• 每个对象的属性指针
• 普通对象数组的每个元素指针
当然,压缩也不是万能的,针对一些特殊类型的指针,JVM是不会优化的。
比如指向PermGen的Class对象指针,本地变量,堆栈元素,入参,返回值,NULL指针不会被压缩。
2. 怎么启用?
在启动java时,加 -XX:+UseCompressedOops (需要jdk1.6.0_14)
3. CompressedOops的原理
当对象被读取时,解压,存入heap时,压缩。
4. 零基压缩优化(Zero Based Compressd Oops)
零基压缩是针对压解压动作的进一步优化。
它通过改变正常指针的随机地址分配特性,强制从零开始做分配(需要OS支持),进一步提高了压解压效率。
要启用零基压缩,你分配给JVM的内存大小必须控制在4G以上,32G以下。
如果小于4G,那么JVM会使用低虚拟地址空间(low virutal address space,64位下模拟32位),这样就不需要做压解压动作了。
而对于大于32G,将采用默认的随机地址分配特性,进行压解压。
5. 适用场景
CompressedOops,可以让跑在64位平台下的JVM,不需要因为更宽的寻址,而付出Heap容量损失的代价。
不过,它的实现方式是在机器码中植入压缩与解压指令,可能会给JVM增加额外的开销。
6. 效果
关闭压缩 启用压缩
基本类型 (int) 4 4
引用类型 (Integer) 4 8
数组(new Integer[0]) 16 24
/**
* -XX:+UseCompressedOops: mark/4 + metedata/8 + 4 = 16
* -XX:-UseCompressedOops: mark/8 + metedata/8 + 4 + padding/4 = 24
*/
class A {
int a;
} /**
* -XX:+UseCompressedOops: mark/4 + metedata/8 + 4 + 4 + padding/4 = 24
* -XX:-UseCompressedOops: mark/8 + metedata/8 + 4 + 4 = 24
*/
class B1 {
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
*/
class B2 {
int b2a;
Integer b2b;
}
三、验证
1.
import java.lang.instrument.Instrumentation; public class ObjectSizeFetcher {
private static Instrumentation instrumentation; public static void premain(String args, Instrumentation inst) {
instrumentation = inst;
} public static long getObjectSize(Object o) {
return instrumentation.getObjectSize(o);
}
}
Add the following to your MANIFEST.MF
:
Premain-Class: ObjectSizeFetcher
测试对象
public class Cat {
private int x;
private int y; public static void main(String [] args) {
System.out.println(ObjectSizeFetcher.getObjectSize(new Cat()));
}
}
5.调用
java -javaagent:ObjectSizeFetcherAgent.jar Cat
6.jol-cli-0.8-full.jar 工具
java -jar jol-cli-0.8-full.jar internals java.lang.Long
java -jar jol-cli-0.8-full.jar internals java.util.HashMap
参考:
HotSpotVM 对象机制实现浅析
一个Java对象占用多少字节?
java.lang.instrument: 一个Java对象占用多少字节?的更多相关文章
- [转]new一个Object对象占用多少内存?
我们分解下ArrayList arr = new ArrayList();等同于ArrayList arr = null;//初始化arr = new ArrayList();//实例化这两个过程.初 ...
- J2SE 1.6 特性:java.lang.instrument
1. import java.lang.instrument.Instrumentation; public class ObjectSizeFetcher { private static Inst ...
- java.lang.instrument 中的premain 实现类的个性化加载(附源代码)
背景 想调用ASM API (用于字节码处理的开源API)对字节码进行处理,目标是实现对java程序运行时各种对象的动态跟踪,并进一步分析各个对象之间的关系(研究前提是目前的UML锁阐释的whole- ...
- java.lang.instrument.Instrumentation
java.lang.instrument.Instrumentation 看完文档之后,我们发现这么两个接口:redefineClasses和retransformClasses.一个是重新定义cla ...
- java.lang.instrument使用
Java在1.5引入java.lang.instrument,你可以由此实现一个Javaagent,通过此agent来修改类的字节码即改变一个类. 程序启动之时启动代理(pre-main) 通过jav ...
- java.lang.ClassNotFoundException与java.lang.NoClassDefFoundError的区别
java.lang.ClassNotFoundException与java.lang.NoClassDefFoundError的区别 以前一直没有注意过这个问题,前两天机缘巧合上网查了一下,然后自 ...
- Java源码学习 -- java.lang.StringBuilder,java.lang.StringBuffer,java.lang.AbstractStringBuilder
一直以来,都是看到网上说“ StringBuilder是线程不安全的,但运行效率高:StringBuffer 是线程安全的,但运行效率低”,然后默默记住:一个是线程安全.一个线程不安全,但对内在原因并 ...
- ### Error querying database. Cause: java.lang.IllegalArgumentException: invalid comparison: cn.xiaojian.blog.po.BlogType and java.lang.String ### Cause: java.lang.IllegalArgumentException: ...
### Error querying database. Cause: java.lang.IllegalArgumentException: invalid comparison: cn.xiaoj ...
- java.lang.IndexOutOfBoundsException at java.io.FileOutputStream.writeBytes(Native Method)
ss available : /usr/linkapp/data/linkapp/ddn_1440639847758_temp java.lang.IndexOutOfBoundsException ...
随机推荐
- lua 打印 table 拷贝table
-- 打印table function print_lua_table (lua_table, indent) if lua_table == nil or type(lua_table) ~= &q ...
- ORACLE中NVL和COALESCE的区别
nvl(COMMISSION_PCT,0)如果第一个参数为null,则返回第二个参数如果第一个参数为非null,则返回第一个参数 COALESCE(EXPR1,EXPR2,EXPR3...EXPRn) ...
- 使用java语言如何更好的使用多线程?
① 高并发.任务执行时间短的业务,线程池线程数可以设置为CPU核数+1,减少线程上下文的切换. ② 并发不高.任务执行时间长的业务要区分开看: 假如是业务时间长集中在I/O操作上,也就是I/O密集型的 ...
- lua语言介绍
什么是Lua Lua是一个小巧的脚本语言. 是巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个研究小组,由Rober ...
- android webview处理h5打开本地文件浏览器的功能
这周遇到一个比较棘手的问题,需要在android上边集成h5页面,并且在h5页面上,需要用户能够上传android本地的照片,一开始我以为webview会自动处理掉的,因此没太留意,当真正集成时,才发 ...
- 第一百五十六节,封装库--JavaScript,延迟加载
封装库--JavaScript,延迟加载 延迟加载的好处,就是在浏览器视窗外的图片,不加载,拖动鼠标到浏览器视窗内后加载,用户不看的图片就不用加载,可以减少服务器大量流量 将图片的src地址用一张统一 ...
- JavaScript能否操作cookie和session?
JavaScript能否操作cookie和session? 解答:JavaScript可以操作cookie,但是不能操作session
- Ubuntu apt-get update错误解决
用apt-get命令安装开发软件非常方便,但由于各种原因,经常链接不上软件源,于是需要使用sudo apt-get update命令来更新软件源. 而屋漏偏逢连夜雨,这时候更新命令也罢工,出现各种错误 ...
- css !import
!important是CSS1就定义的语法,作用是提高指定样式规则的应用优先权,语法格式{ sRule!important },即写在定义的最后面,并且在分号之前,比如: {font-color: r ...
- 剑指offer(13)-栈的压入、弹出序列 九度1366
题目来自剑指offer系列 九度 1366:http://ac.jobdu.com/problem.php?pid=1367 题目描述: 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列 ...