CPU、内存

本文深入探讨了Java中的volatile关键字如何防止指令重排序,确保多线程环境下的数据一致性。同时,介绍了JVM的类加载机制,包括双亲委派模型以及如何打破这一模型。此外,还详细阐述了类的加载、链接、初始化过程,并分析了CPU缓存行对齐与伪共享问题,以及如何解决乱序问题以保证指令执行的有序性。
摘要由CSDN通过智能技术生成

volatile 是禁止指令重排序(DCL单例使用)

class T {
	int m = 8;
}
T t = new T();

在这个new对象的过程中有几种状态
几条指令
申请内存空间赋值初始值–>复制栈中内容–>消耗栈中一个(弹出来)指向的对象调用构造方法(消耗一个引用)—>弹出栈赋值给局部变量(astore-1;astore-0是this)

new对象的过程

初始化有中间态(半初始化),有可能会出现指令重排序
Java有规定一些情况下不允许出现指令重排序(如:多线程时提前进来,使用了半初始化的对象)
其他都没有规定
CPU禁止指令重排序: 内存屏障(原语’mfence lfence sfence’ 或者锁总线)
JVM禁止指令重排序: JSR内存屏障(8个hanppens-before原则 4个内存屏障 LL LS SL SS)

new对象过程

new对象过程

JVM重排序规则

1.程序次序规则:
同一个线程内,按照代码出现的顺序,前面的代码先于后面的代码,准确来说是控制流顺序,因为要考虑到分支和循环结构.
2.管程锁定规则:
一个unlock操作先行发生于后面(时间上)对同一个锁的lock操作.
3.volatile变量规则:
对一个volatile变量的写操作先行发生于后面(时间上)对这个变量的读操作
4.线程启动规则:
thread的start()方法先行发生于这个线程的每一个操作.
5.线程终止规则:
线程的所有操作都先行于此线程的终止检测.可以通过thread.join()方法结束、thread.isAlive()的返回值等手段检测线程的终止.
6.线程中断规则:
对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事见的发生.可以通过thread.interrupt()方法检测线程是否中断
7.对象终结规则:
一个对象的初始化完成先行于发生它的finalize()方法的开始.
8.传递性:
如果操作A先行于操作B,操作B先行于操作C,那么操作A先行于操作C

JVM重排序规则

jvm底层

双亲委派

一个孩子向父亲方向;然后父亲向孩子方向的双亲委派过程

父加载器不是类加载器的加载器;也不是父类加载器的父类加载器

getClassLoader().getParent(); 获取类加载器.类加载器的父加载器

classLoader加载时向上询问是否已经有加载,没有就继续向上询问,询问结果返回,如果有则直接拿来用、若都没有则重新加载;
实现:主要是安全;其次是防止资源浪费(已经加载则不需要再加载)

打破双亲委派

打破双亲委派
1.如何打破:重写loadClass()
2.何时打破过:

1.JDK1.2之前,自定义ClassLoader都必须重写loadClass()
2.ThreadContextClassLoader可以实现基础类调用实现类代码,通过thread.setContextClassLoader
3.热启动,热部署–>加载同一类库的不同版本
热加载会把classloader文件干掉重写加载一个

在这里插入图片描述

自定义类加载器

继承ClassLoader类—>底层调用还是loderClass
重写findClass

public class JvmCode extends ClassLoader {
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        return super.findClass(name);
    }
}
class文件加密
    /**
     * 文件加密
     */
    private static void encFile(String name) throws IOException, InterruptedException {
        File file = new File("G:\\新建文件夹 (2)\\新建文件夹", name.replace('.', '/').concat(".class"));
        FileInputStream fis = new FileInputStream(file);
        FileOutputStream fos = new FileOutputStream(new File("G:\\新建文件夹 (2)\\新建文件夹", name.replaceAll(".", "/").concat(".szjclass")));
        int b = 0;
        while ((b = fis.read()) != -1) {
            fos.wait(b ^ seed);
        }
        fis.close();
        fos.close();
    }

类(Loading)、链接(Linking)初始化(Initialization)

1.Verification

验证文件是否符合JVM规定

2.Preparation(赋默认值)

静态成员变量赋默认值

3.Resolution(解析)

将类、方法、属性等符号引用解析为直接引用
常量池中的各种符号引用解析为指针、偏移量等内存地址的直接引用

Initializing初始化

静态变量赋初始值

调用类初始化

在这里插入图片描述

CPU

cache line(缓存行对齐、伪共享)

缓存行:64字节

现代CPU数据一致性:缓存锁(MESI协议等)+锁总线

每次发送自动补全字节,一次性发送,目的提高效率
位于同一缓存行的两个不同数据,被两个不同CPU锁定,产生互相影响的的伪共享问题
熟练使用缓存行的对齐能提高效率

乱序问题

CPU速度很快: 执行一条指令(去内存中读取数据–>慢100倍),等待指令返回的时间比较浪费;
在等待一条指令的同时,可运行其他指令,返回则继续运行.以此类推
前提是其他指令与这条指令没有依赖关系

WCbuffer: 写缓存区;
速度:CPU>WCbuffer(一般4个位置 太珍贵)>L1>L2>L3>内存>硬盘>云储存
写的同时可以合并写
写操作可以进行合并–>在写入一个数据时由于太慢了,后续的写入又进来,最终会将他们合并在一个缓存中计算得出

如何保证特定情况下CPU指令不乱序

volatile有序

实现细节

字节码层面

ACC_VOLATILE

JVM层面

volatile内存区的读写,都加内存屏障
在读写前后都加,不允许重排序>在这里插入图片描述

os和硬件层面

使用hsdis观察汇编码
hsdis---->HostSpot Dis Assembler
虚拟机的反汇编

硬件层面(内存屏障)x86

Inter CPU内存屏障:
sfence:store—>在sfence指令前的写操作当必须在sfence指令后的写操作前完成
ifence:load—>ifence指令前的读操作当必须在ifence指令后的读操作前完成
mfence:modify/min—>mfence指令前的读写操作当必须在mfence指令后的读写操作前完成

软件层面(JVM规范)

JVM规范:
1.LoadLoad屏障:

对于这样的语句Load1;LoadLoad;Load2
在load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕.

2.StoreStore屏障:

对于这样的语句Store1;StoreStore;Store2,
在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见.

3.LoadStore屏障:

对于这样的语句Load1;LoadStore;Store2,
在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕.

4.StoreLoad屏障:

对于这样的语句Store1;StoreLoad;Load2,
在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值