并发编程专题-基础-11、乱序问题
1. 乱序问题1.1. 乱序来源 编译器优化的重排序。编译器在 不改变单线程程序语义 的前提下,可以重新安排语句的执行顺序。 指令级并行的重排序。现代处理器采用了指令级并行技术(Instruction-Level Parallelism,ILP)来将多条指令重叠执行。如果不存在 数据依赖性,处理器可以改变语句对应机器指令的执行顺序。 内存系统的重排序。由于处理器使用缓存和读/写缓冲区,这使得加载和存储操作看上去可能是在乱序执行。 1 属于 编译器重排序,2 和 3 属于 处理器重排序。从 Java 源代码到最终实际执行的指令序列,会分别经历下面 3 种重排序: 1. 即时编译器优化2. 指令执行乱序3. 内存写入乱序 1.1.1. 即时编译优化1.1.1.1. C 语言volatile 和编译器屏障都是通过禁止 即时编译优化 来保证可见性的 编译器屏障 1.1.1.2. Java 编译期:像 c/c++ 只有一个编译期,就是调用 gcc 命令将 c/c++ 代码编译成汇编代码。但是 Java 中有两个编译期:1、调用 javac 命令将 Java 代 ...
并发编程专题-基础-10、内存屏障
其实从头看到尾就会发现,一个技术点的出现往往是为了填补另一个的坑。为了解决处理器与主内存之间的速度鸿沟,引入了高速缓存,却又导致了缓存一致性问题为了解决缓存一致性问题,引入了如MESI等技术,又导致了处理器等待问题为了解决处理器等待问题,引入了写缓冲和无效化队列,又导致了重排序和可见性问题为了解决重排序和可见性问题,引入了内存屏障 1. 屏障分类为何而来?为了解决计算机的乱序问题而来。那为什么要有乱序,为了性能,ok。那有什么乱序需要用屏障来避免?并发基础-11、乱序问题 1.1. 编译器屏障(优化屏障)Optimization barrier,解决编译器优化乱序问题。 编译器编译源代码时,会将源代码进行优化,将源代码的指令进行重排序,以适合于CPU的并行执行。然而,内核同步必须避免指令重新排序,优化屏障(Optimization barrier)是避免编译器的重排序优化操作,保证编译程序时在优化屏障之前的指令不会在优化屏障之后执行。Linux用宏barrier实现优化屏障,gcc编译器的优化屏障宏定义列出如下(在include/linux/compiler-gcc ...
性能调优专题-基础-9、JVM-HSDB
1. 反编译 反汇编https://juejin.im/post/5b72860e51882560eb6b5269 https://www.jianshu.com/p/6a8997560b05 1.1. 反编译将 .class 文件逆向成 java源代码 1.2. 反汇编或者叫字节码解析 1、javap是jdk自带的反解析工具。它的作用就是根据class字节码文件,反解析出当前类对应的code区(汇编指令)、本地变量表、异常表和代码行偏移量映射表、常量池等等信息。 一般常用的是-v -l -c三个选项。 javap -v classxx,不仅会输出行号、本地变量表信息、反编译汇编代码,还会输出当前类用到的常量池等信息。 javap -l 会输出行号和本地变量表信息。 javap -c 会对当前class字节码进行反编译生成汇编代码 安装了Java开发环境的电脑上,可以通过命令⾏ 输⼊ :javap -c XXX.class ⽂件来查看该class⽂件的编 译过程。 javap的⽤法格式: javap 其中classes就是你要反编译的class⽂件。 在命令⾏中直接输⼊javap或 j ...
001-基础知识专题-关键字和接口-3、String和StringTable
1. String 基本特性String: 代表不可变的字符序列。简称: 不可变性。 当对字符串 重新赋值 时,需要重写指定内存区域赋值,不能使用原有的 value 进行赋值。 当对现有的字符串进行 连接操作 时,也需要重新指定内存区域赋值,不能使用原有的 value 进行赋值。 当调用 String 的 replace() 方法修改 指定字符或字符串时,也需要重新指定内存区域赋值,不能使用原有的 value 进行赋值。 String 实现了 Serializable 接口: 表示字符串是支持序列化的。实现了 Comparable 接口: 表示 String 可以比较大小 通过 字面量的方式(区别于 new) 给一个字符串赋值,此时的字符串值 声明在字符串常量池中。 2. 存储结构变更String 在 jdk8 及以前内部定义了 final char[] value 用于存储字符串数据。jdk9 时改为 final byte[] value 3. StringTable%%▶2.🏡⭐️◼️【🌈费曼无敌🌈⭐️第一步⭐️】◼️⭐️-point-20230427-1530%%❕ ...
性能调优专题 - 基础 -7、JVM- 堆和对象创建
1. 堆结构JDK7 中永久代到了 JDK8 中变成元空间 2. 调整参数配置新生代与老年代在堆结构的占比。 默认 -XX:NewRatio=2,表示新生代占 1,老年代占 2,新生代占整个堆的 1/3 可以修改 -XX:NewRatio=4,表示新生代占 1,老年代占 4,新生代占整个堆的 1/5 在 HotSpot 中,Eden 空间和另外两个 survivor 空间缺省所占的比例是 8:1:1 当然开发人员可以通过选项“-xx:SurvivorRatio”调整这个空间比例。比如 -xx:SurvivorRatio=8 几乎所有的 Java 对象都是在 Eden 区被 new 出来的。绝大部分的 Java 对象的销毁都在新生代进行了。 IBM 公司的专门研究表明,新生代中 80% 的对象都是“朝生夕死”的。 可以使用选项 “-Xmn“ 设置新生代最大内存大小,这个参数一般使用默认值就可以了。 -Xmx3550m:设置 JVM 最大可用内存为 3550M。-Xms3550m:设置 JVM 促使内存为 3550m。此值可以设置与 -Xmx 相同,以避 ...
性能调优专题-基础-5、JVM-虚拟机栈
1. 总体结构 2. 基本内容2.1. 线程独立每个线程对应各自的虚拟机栈,线程独立 2.2. 大小设置 3. 栈帧3.1. 基本内容 3.2. 运行原理 3.3. 本地变量表3.3.1. 基本内容用来存储形参、局部变量、返回值等内容 3.3.2. 作用影响主要影响栈帧大小,进而影响栈帧个数,以及是否容易发生溢出 3.3.3. Slot 运行原理 本地变量表 (局部变量表) 是重要的垃圾回收根节点 3.4. 操作数栈 3.5. 动态链接⭐️🔴 3.5.1. class 文件常量池和运行时常量池性能调优专题-基础-3、JVM-方法区和3个池子 3.5.2. 静态绑定和动态绑定将所调用方法的符号引用转为直接引用的方式有 2 种:静态绑定和动态绑定 3.5.3. 虚方法和非虚方法 静态方法不能重写: https://blog.csdn.net/gao_zhennan/article/details/72892946 JDK7新增加invokedynamic方法 invokestatic, invokespecial 指令调用的方法为非虚方法invokevirt ...
性能调优-基础-6、JVM-类加载器
1. 类加载器分类 1.1. 启动类加载器⭐️🔴 1.2. 扩展类加载器 1.3. 系统类加载器 1.4. 线程上下文类加载器⭐️🔴1.4.1. 是什么ContextClassLoader 是一种与线程相关的类加载器,类似 ThreadLocal,每个线程对应一个上下文类加载器,主要是用了打破类加载器中的委托机制的。在 SPI 中用来加载实现类。 线程上下文类加载器(Thread Context ClassLoader)可以通过 java.lang.Thread 类的 setContextClassLoader() 方法人为设置,创建线程时候未指定的话,则默认从父线程中继承。 那父线程中也没指定呢?那么会默认为应用程序的类加载器。例如:main 方法的线程上下文类加载器就是 sun.misc.Launcher$AppClassLoader。 1.4.2. 用途SPI 的接口是 Java 核心库的一部分,是由 引导类加载器(Bootstrap Classloader) 来加载的;SPI 的实现类是由 系统类加载器 来加载的。 1.4.3. 设置时机⭐️🔴为什么默认的线程上下 ...
性能调优专题-基础-3、JVM-方法区和3个池子
方法区也需要进行垃圾回收,大约5%的垃圾回收发生在方法区,但回收率并不高。只有在full GC时才会回收。 1. 不同实现 JDK7用永久代实现方法区 JDK8后用元空间实现方法区,并且将方法区中的字符串常量池和类的静态变量放到堆中,只保留下类的元数据信息(方法元信息、类元信息) 2. 内存设置2.1. 默认大小#todo - [ ] 🚩 - 方法区笔记细化:https://www.yuque.com/u21195183/jvm/nbkm46 - 🏡 2023-01-26 20:14 2.2. 内存溢出1.加载类的二进制字节码 2.jdk1.8设置元空间大小模拟方法区内存溢出 -XX:MaxMetaspaceSize=8m 3.jdk1.6设置永久代大小模拟方法区内存溢出 -XX:MaxPermSize=8m 框架中的cglib会产生大量的字节码 3. 内部结构3.1. 类型信息对每个加载的类型(类class、接口interface、枚举enum、注解annotation),JVM必须在方法区中存储以下类型信息: 这个类型的完整有效名称(全名 ...
性能调优专题-基础-4、JVM-堆和GC
1. 分类及触发条件⭐️🔴1.1. 堆分区及对应 GC 性能调优-进阶-1、JVM-GC调优 1.2. Minor GC(YGC)❕%%1827-🏡⭐️◼️新生代 GC 的特点是什么?新生代分为 Eden,S0,S1,Eden 满了就发生 YGC,新生代 GC 是非常频繁的,Survivor 区满了不会发生 GC,是被动 GC。YGC 会引发短时间的 STW◼️⭐️-point-202301281827%% 1.3. Major GC(Old GC) 1.4. Full GC⭐️🔴 1.4.1. System.gc() 方法的调用此方法的调用是建议 JVM 进行 Full GC, 虽然只是建议而非一定, 但很多情况下它会触发 Full GC, 从而增加 Full GC 的频率, 也即增加了间歇性停顿的次数。强烈影响系建议能不使用此方法就别使用,让虚拟机自己去管理它的内存,可通过通过 -XX:+ DisableExplicitGC 来禁止 RMI 调用 System.gc。 1.4.2. 老年代代空间不足老年代空间只有在新生代对象转入及创建为大对象、大数组时才会出现不足的现象 ...
001-基础知识专题-关键字和接口-2、final
1. final 的内存语义 在构造函数内对一个 final 域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。也就是说只有将对象实例化完成后,才能将对象引用赋值给变量。 初次读一个包含 final 域的对象的引用,与随后初次读这个 final 域,这两个操作之间不能重排序。也就是下面示例的 4 和 5 不能重排序。 当 final 域为引用类型时,在构造函数内对一个 final 引用的对象的成员域的写入,与随后在构造函数外把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。 下面通过代码在说明一下: 123456789101112131415161718192021222324public class FinalExample { int i; // 普通变量 final int j; // final变量 static FinalExample obj; public FinalExample() { // 构造函数 i = 1;// 写普通域 j = ...


