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 ...
随机推荐
- 李洪强和你一起学习前端之(7)定位盒子 css可见性 滑动门案例
今天是2017年3月23日 1 复习昨天知识 1.1浮动 Float:left | right 特点: ->浮动的元素不占位置(脱标) ->可以将行内元素转化为行内块元素 ->块级元 ...
- react-native在win10下用adb报错的解决方案
react-native在WIN10上面运行用adb链接模拟器,会直接在powershell里报错, 报错大概意思是识别不了adb这个cmdlet函数. 找了很久的解决办法,直接找到adb.exe所在 ...
- shell script 在if 的判断条件正则表达式=~中引号问题
今天在脚本里运行if判断的时候,总是进不了对应的分支,检查正则表达式也没有错误.单独拿到shell里面执行还是显示没有匹配.比较奇怪,就搜了下,才发现是在=~ 后面的正则表达式上不能加上引号,而且以点 ...
- spring 集成 redis -- pub/sub
redis除了常用的当做缓存外,还可以当做简单的消息中间件,实现消息发布订阅 spring集成redis,可以使用spring-data-redis 首先引入相关maven依赖(此处我spring相关 ...
- 基于quick-cocos2d-x的LuaSocket范例
这是一个 luasocket 范例. 为了便于使用,我封装了 luasocket 到 cc.net.SocketTCP 类中.这个范例展示如何使用 cc.net.SocketTCP . 同时,在本范例 ...
- 使用 Google Guava 美化你的 Java 代码:1~4(转)
参考:https://my.oschina.net/leejun2005/blog/172328 记录不做具体转载了
- Servlet Servlet是Java平台上的CGI技术
Servlet Servlet是Java平台上的CGI技术.Servlet在服务器端运行,动态地生成Web页面.与传统的CGI和许多其它类似CGI的技术相比,Java Servlet具有更高的效率并更 ...
- Centos 下面安装 docker
试过了虚拟机VM ,今天尝试在虚拟机centos 中安装 docker ,入门是看的阮一峰的http://www.ruanyifeng.com/blog/2018/02/docker-tutorial ...
- 使用apt-get命令,出现“无法获得锁”的问题
参考地址 http://blog.csdn.net/namecyf/article/details/7297168
- MATLAB使用fft求取给定音频信号的频率
一段10s立体声音频,采样率位8000Hz,已知频率为1000Hz clc; clear; [data, Fs] = audioread('1khz_stereo_8000.wav'); fs=Fs; ...