class文件 动态代理 静态代理
javac 编译 .java文件 为 class文件,后面.class文件转换为 机器指令
现代CPU下程序运行
多CPU –>CPU寄存器 –> 高速缓存cache –> 内存RAM
缓存一致性
多个线程可能不在一个CPU中运行,那么对于的变量可能不太一致,需要同步 volatile
指令重排序
对编译的时候对代码进行重排序,优化执行的效率,但是是一个变量,多个线程去执行的时候也行会导致,执行的变量的问题,不是说这个重排序是错的比如说不相干的变量查询,可能在CPU执行过程中可能有先后顺序的差异.
逃逸分析
相当于 你方法内的变量 需要传递出去,给别人用,不是基础类型
1 | public static StringBuffer craeteStringBuffer(String s1, String s2) { |
第一段代码中的sb
就逃逸了,而第二段代码中的sb
就没有逃逸。
使用逃逸分析,编译器可以对代码做如下优化:
一、同步省略。如果一个对象被发现只能从一个线程被访问到,那么对于这个对象的操作可以不考虑同步。
二、将堆分配转化为栈分配。如果一个对象在子程序中被分配,要使指向该对象的指针永远不会逃逸,对象可能是栈分配的候选,而不是堆分配。
三、分离对象或标量替换。有的对象可能不需要作为一个连续的内存结构存在也可以被访问到,那么对象的部分(或全部)可以不存储在内存,而是存储在CPU寄存器中。
作者:HollisChuang
链接:https://juejin.cn/post/6844903639308304397
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
逃逸分析优化
针对上面第三点,当一个对象没有逃逸时,可以得到以下几个虚拟机的优化。
1) 锁消除
我们知道线程同步锁是非常牺牲性能的,当编译器确定当前对象只有当前线程使用,那么就会移除该对象的同步锁。
例如,StringBuffer 和 Vector 都是用 synchronized 修饰线程安全的,但大部分情况下,它们都只是在当前线程中用到,这样编译器就会优化移除掉这些锁操作。
锁消除的 JVM 参数如下:
- 开启锁消除:-XX:+EliminateLocks
- 关闭锁消除:-XX:-EliminateLocks
锁消除在 JDK8 中都是默认开启的,并且锁消除都要建立在逃逸分析的基础上。
2) 标量替换
首先要明白标量和聚合量,基础类型和对象的引用可以理解为标量,它们不能被进一步分解。而能被进一步分解的量就是聚合量,比如:对象。
对象是聚合量,它又可以被进一步分解成标量,将其成员变量分解为分散的变量,这就叫做标量替换。
这样,如果一个对象没有发生逃逸,那压根就不用创建它,只会在栈或者寄存器上创建它用到的成员标量,节省了内存空间,也提升了应用程序性能。
标量替换的 JVM 参数如下:
- 开启标量替换:-XX:+EliminateAllocations
- 关闭标量替换:-XX:-EliminateAllocations
- 显示标量替换详情:-XX:+PrintEliminateAllocations
标量替换同样在 JDK8 中都是默认开启的,并且都要建立在逃逸分析的基础上。
3) 栈上分配
当对象没有发生逃逸时,该对象就可以通过标量替换分解成成员标量分配在栈内存中,和方法的生命周期一致,随着栈帧出栈时销毁,减少了 GC 压力,提高了应用程序性能。
happen-before原则
程序顺序规则:一个线程中的每个操作,happens-before于该线程中的任意后续操作。
监视器锁规则:对一个锁的解锁,happens-before于随后对这个锁的加锁。
volatile变量规则:对一个volatile域的写,happens-before于任意后续对这个volatile域的读。
传递性:如果A happens-before B,且B happens-before C,那么A happens-before C。
GC垃圾回收
定义垃圾
- 引用计数算法
- 可达性分析算法
内存中的分配
主要是手机heap(堆内存)主要是堆内存最大,同时是零碎的不像 stack(栈内存)是在方法调用完或者方法的引用完后就自动回收的.
目前堆内存中主要是初始化好的对象实例,和对象实例的类里面的变量,
虚拟机栈(栈帧中的本地变量表)中引用的对象
方法区中类静态属性引用的对象
方法区中常量引用的对象
本地方法栈中 JNI(即一般说的 Native 方法)引用的对象
Java 堆(Java Heap)是JVM所管理的内存
清除方式
1.标记清除算法
…
2.复制算法
…
3.标记整理算法
…
参考链接: