目录
保证特定情况下不乱序
Volatile
Volatile的实现细节(字节码层(源码刚编译完,变量包含Access flage)、JVM层(虚拟机读到flage的时候明也就是 Access flage))
- 字节码层面
Access flage 代表变量j签面的修饰。 j中包含了Volatile。 ----- ACC_VOLATILE - JVM层
Volatile内存区的读写都加入JVM的内存屏障
。
JVM规范如下包含四层屏障:
3.OS和硬件层面
得用hsdis(虚拟机的反汇编)观察汇编码。 省略…
Synchornize的实现细节
- 字节码层面
ACC_SYNCHRONIZED. – 修饰方法
monitorenter { 运行代码} monitorexit – 定义代码块 - JVM层面
C 和 C++ 调用操作系统提供的同步机制。 - OS和硬件层面
x86:lcok comxchg xxx
对象在内存中的存储布局,以及大小 ?
观察虚拟机配置
java -XX:+PrintCommandLineFlags -version
其中: -XX:+UseCompressedClassPointers。表示压缩指针打开,把原本8个字节的classPointers压缩为4个字节。
-XX:+UseCompressedOops:压缩普通成员变量指针。比如成员变量,String name。本身占8个字节,开启Oops指针压缩后只占4个字节。
对象大小(64位机)
对象的内存布局分为:普通对象和数组对象
普通对象包含4个部分
- 对象头:称为markword 8个字节
- ClassPointer指针: 他指向对应所属于的.Class文件
-XX:+UseCompressedClassPointers 为4字节 不开启为8字节. - 实例数据(指的是成员变量)
引用类型:-XX:+UseCompressedOops 为4字节 不开启为8字节 Oops Ordinary Object Pointers - Padding对齐,8的倍数。(64个字节是按块读的,一下子读8的倍数效率更高)
数组对象
- 对象头:markword 8
- ClassPointer指针同上
- 数组长度:4字节
- 数组数据
- 对齐 8的倍数
实验
-
新建项目ObjectSize (1.8)
-
创建文件ObjectSizeAgent
package com.mashibing.jvm.agent;
import java.lang.instrument.Instrumentation;
public class ObjectSizeAgent {
private static Instrumentation inst;
public static void premain(String agentArgs, Instrumentation _inst) {
inst = _inst;
}
public static long sizeOf(Object o) {
return inst.getObjectSize(o);
}
}
- src目录下创建META-INF/MANIFEST.MF
Manifest-Version: 1.0
Created-By: mashibing.com
Premain-Class: com.mashibing.jvm.agent.ObjectSizeAgent
注意Premain-Class这行必须是新的一行(回车 + 换行),确认idea不能有任何错误提示
-
打包jar文件
-
在需要使用该Agent Jar的项目中引入该Jar包
project structure - project settings - library 添加该jar包 -
```运行时需要该Agent Jar的类``,并加入参数,指定某个jar文件当作代理来运行这次的虚拟机:
-javaagent:C:\work\ijprojects\ObjectSize\out\artifacts\ObjectSize_jar\ObjectSize.jar -
如何使用该类:
public class T03_SizeOfAnObject {
public static void main(String[] args) {
System.out.println(ObjectSizeAgent.sizeOf(new Object()));
System.out.println(ObjectSizeAgent.sizeOf(new int[] {}));
System.out.println(ObjectSizeAgent.sizeOf(new P()));
}
private static class P {
//8 _markword
//4 _oop指针
int id; //4
String name; //4
int age; //4
byte b1; //1
byte b2; //1
Object o; //4
byte b3; //1
}
}
输出结果。object 16个字节,怎么组成的?
- +8个对象头
- +4个Classpointer(原本8个字节被压缩成4个字节 – 默认打开的XX:+UseCompressedClassPointers)
- +4 padding
指针压缩。
对象头具体包括什么?
对象头8个字节等于32个bit
32位的图:(1、hash,2、分代年龄,3、偏向锁、3、锁标志)
以上的图根下面画出来的
在这里插入图片描述
主要分析GC标记 和锁标志位。
标志位分析 – 联合Synchronized锁升级
- 首先是无锁状态(也就是偏向锁位置为1)01
- 当有多个线程时 变成轻量级锁。 00
- 当轻量级锁自旋一定次数。升级成为重量级锁。10
对象的Hashcode。
Hashcode存在黑马和白马的问题。
- 白马:如果原始hashcode,也就是根据
内存布局计算
出来的。 我们称之为identifyCode
。->可根据System.identityHashCode(…)得到 - 黑马:
轻量级锁
轻量级锁作用在JVM栈中,并没有涉及到内核。
分带年龄
因为32位机,分代年龄4个bit。 则GC年龄默认为15.
偏向锁
当JAVA处在偏向锁、重量级锁状态时,hashCode值存在哪里?
- 当对象已经计算过identity hash code就无法进入偏向锁状态。因为对象的hashCode经过了计算会占用偏向锁中(线程ID和Epoch)的信息。所以偏向锁进不来。
一旦调用hashcode方法,锁的状态一定不再是偏向状态。
(调用hashCode(),对象头才有hashCode值,不调用hashCode(),对象头没有hashCode值)
对象如何定位?
1、句柄池
过程:1、先指向间接指针:该指针一个包含具体对象的地址,一个包含该对象的.Class文件。
优点: 在CMS产生三色标级的时产生的计算,效率更好。
2、直接指针(hotspot使用该方法定位)
过程:1、先指向具体地址,2、具体地址里面含有该模版类的指针
优点:对象找内存效率高