内存屏障和JVM相关指令,内存布局等

保证特定情况下不乱序

Volatile

在这里插入图片描述

Volatile的实现细节(字节码层(源码刚编译完,变量包含Access flage)、JVM层(虚拟机读到flage的时候明也就是 Access flage))

  1. 字节码层面
    Access flage 代表变量j签面的修饰。 j中包含了Volatile。 ----- ACC_VOLATILE
  2. JVM层
    在这里插入图片描述
    Volatile内存区的读写都加入JVM的内存屏障
    JVM规范如下包含四层屏障:
    在这里插入图片描述
    3.OS和硬件层面
    得用hsdis(虚拟机的反汇编)观察汇编码。 省略…

Synchornize的实现细节

  1. 字节码层面
    ACC_SYNCHRONIZED. – 修饰方法
    monitorenter { 运行代码} monitorexit – 定义代码块
  2. JVM层面
    C 和 C++ 调用操作系统提供的同步机制。
  3. OS和硬件层面
    x86:lcok comxchg xxx

对象在内存中的存储布局,以及大小 ?

观察虚拟机配置
java -XX:+PrintCommandLineFlags -version
在这里插入图片描述

其中: -XX:+UseCompressedClassPointers。表示压缩指针打开,把原本8个字节的classPointers压缩为4个字节。

-XX:+UseCompressedOops:压缩普通成员变量指针。比如成员变量,String name。本身占8个字节,开启Oops指针压缩后只占4个字节。

对象大小(64位机)

对象的内存布局分为:普通对象和数组对象

普通对象包含4个部分

  1. 对象头:称为markword 8个字节
  2. ClassPointer指针: 他指向对应所属于的.Class文件
    -XX:+UseCompressedClassPointers 为4字节 不开启为8字节.
  3. 实例数据(指的是成员变量)
    引用类型:-XX:+UseCompressedOops 为4字节 不开启为8字节 Oops Ordinary Object Pointers
  4. Padding对齐,8的倍数。(64个字节是按块读的,一下子读8的倍数效率更高)

数组对象

  1. 对象头:markword 8
  2. ClassPointer指针同上
  3. 数组长度:4字节
  4. 数组数据
  5. 对齐 8的倍数

实验

  1. 新建项目ObjectSize (1.8)

  2. 创建文件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);
    }
}
  1. src目录下创建META-INF/MANIFEST.MF
Manifest-Version: 1.0
Created-By: mashibing.com
Premain-Class: com.mashibing.jvm.agent.ObjectSizeAgent

注意Premain-Class这行必须是新的一行(回车 + 换行),确认idea不能有任何错误提示

  1. 打包jar文件

  2. 在需要使用该Agent Jar的项目中引入该Jar包
    project structure - project settings - library 添加该jar包

  3. ```运行时需要该Agent Jar的类``,并加入参数,指定某个jar文件当作代理来运行这次的虚拟机:
    -javaagent:C:\work\ijprojects\ObjectSize\out\artifacts\ObjectSize_jar\ObjectSize.jar

  4. 如何使用该类:

   
   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. 首先是无锁状态(也就是偏向锁位置为1)01
  2. 当有多个线程时 变成轻量级锁。 00
  3. 当轻量级锁自旋一定次数。升级成为重量级锁。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、具体地址里面含有该模版类的指针
优点:对象找内存效率高

对象创建过程

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值