1内存模型
1.1方法区
- 存储着static final常量,类的信息,静态变量,最重要的是常量池:存放编译后生成的字面量(文本字符串,final的常量)和符号引用。String类的intern方法可以向常量池增加信息。
1.2Java堆
- 常见设置参数
- -Xms 初始堆大小
- -Xmx 最大堆大小
- -Xmn 年轻代=eden+survivor
- -XX:PermSize 持久代初始大小
- -XX:MaxPermSize 持久代最大值
- -XX:NewRadio 老年代与年轻代比值
- -XX:SurvivorRadio Eden与Survivor比值
- -XX:MaxTenuringThreshold 进入老年代Age年龄设置
- -XX:PretenureSizeThreshold 对象多大直接分配老年代
- -XX:HandlePromotionFailure 是否允许担保失败
- 存放用户生成的对象实例
- 分区:多种分法,分为新生代,老年代;新生代有Eden空间和两个Survivor空间。如果开启TLAB那么还会分为多个线程私有的分配缓冲区。
1.3程序计数器
- 虚拟机多线程是切换轮流执行,需要程序计数器记录切换后的执行位置
1.4本地方法栈
略
1.5Java虚拟机栈
每个方法执行时就会创建一个栈帧包含:
- 1.局部变量表:编译器可知的基本数据类型,对象引用,局部变量表中的变量没有初始值。
- 2.操作数栈
3.动态链接:存放指向方法的引用,符号引用转换为直接引用的时间不同分为静态链接和动态链接。
- 解析调用:类加载阶段进行:方法程序在真正运行之前就有一个可以确定的版本,在调用时期不可变,即编译期可知,运行时期不可变。主要分为两大类:静态方法,私有方法,实例构造器,父类方法,fianl方法
- 静态分派:依赖静态类型,编译期可得
- 动态分派:根据实际类型。
4.方法出口
1.6直接内存
是JavaNIO使用的缓存区域,可以直接在堆外内存中分配,可以使用反射unsafe类来获得。
2垃圾回收
2.1判断是否需要回收
1.是否有引用,无法解决相互引用问题
2.可达性分析根有四个:
- 虚拟机栈中的本地变脸表中引用的
- 方法区中的静态属性的对象
- 方法区中常量应用的对象
- 本地方法栈中引用的对象
2.2垃圾收集算法
- 标记清除法:碎片 老年代
- 复制算法:空间浪费 新生代
- 标记整理:老年代
2.3 何时回收
安全点:抢先式中断和主动式中断
安全区域:在代码片段中不会引起引用发生变化的时候
2.4垃圾回收器(主要是CMS和G1)
CMS:
- 初始标记(停顿)只标记根直接引用的
- 并发标记 tracing
- 重新标记(停顿)之前改动过的
- 并发清除
占用CPU,每次清理有残余的浮动垃圾,标记清除算法有碎片
G1:
特点:
最重要的改进,对堆进行了分区,避免全堆停止;
维护一个有限列表根据算法确定回收价值最大的区域;
使用一个set记录不同区的对象,避免全堆扫描
优势:
1. 并发
2. 分代
3. 空间整合:因为标记整理
4. 可预测停顿:分区了,可以选择一定的分区回收
过程:
- 初始标记(停顿,跟CMS一样)
- 并发标记
- 最终标记(停顿,根据log更新标记)
- 筛选回收(停顿,制定回收分区所以时间可控)
2.5内存分配策略
- 优先分配Eden区
- 设置超过年龄进入老年代
- 大对象直接进入老年代
- 分配担保策略
3实战ecplice调优(加深理解)
- Java运行时编译
- 增加Eden区的大小,减少MinorGc次数
- 增加老年代大小,减少FullGC次数
4类加载机制
4.1加载
- 双亲委派机制
- 加载器分为:启动类加载器,扩展类加载器,应用程序类加载器
- 使用组合方式来复用父类加载器代码
- 这样加载类的时候会有一种优先级的区别,类加载器加载类时首先先让父类去加载,然后再自己加载
- 双亲委派机制破坏
- JNDI
- 动态语言支持,热插拔
4.2验证
4.3准备
类变量(static修饰的,final static修饰的是给定的值)赋初始值,分配在方法区中,实例变量不赋值。
4.4解析
将符号引用转换问直接引用。(像是老师点名记录学生坐在哪里)
4.5初始化
- 五种情况:
- 1.new,设置、读取静态字段,访问静态方法
- 2.反射调用
- 3.初始化子类时父类为初始化,但是接口是不同的。
- 4.加载main()
5.动态语言支持的MethodHandle实例
误区:
- 1.子类调用继承的静态字段和静态方法不会初始化子类。
- 2.数组定义引用类不会导致类初始化。
3.常量引用不会导致初始化。
顺序
- 搜集顺序是按照源文件顺序,静态语句块中的语句只能赋值后面的类变量,不能访问。
- 父类先于子类运行
- 是线程安全的,同一个类加载器对同一个类只加载一次
4.6字节码和动态代理技术
需要一个接口,被代理的类继承这个接口,创建实现InvocationHandler的动态代理类,实现里面的invoke(Object proxy,Method method, Object[] args)方法。调用NewProxyInstance();每一个方法,都会调用invoke方法,这样可以实现切面编程AOP
5语法糖
1.伪泛型
在源代码中的泛型,在code属性中的字节码删除,但是元数据中还是保留了。
2.自动装箱,拆箱
3.foreach循环遍历
6逃逸分析
分析对象动态的作用域,如果对象在方法中定义之后被外部的方法引用叫方法逃逸,类似还有线程逃逸。对于未逃逸的对象我们可以采用一些办法加快运行速度:1.栈上分配,2.同步消除,3.标量替换
7高效并发
7.1Java内存模型
线程,工作内存,save和load操作,主内存
7.2volatile
使用场景:
- 运算结果并不依赖变量的当前值,或者能够确保只有单一的线程修改变量的值。
- 变量不需与其他的状态变量共同参与不变约束。
作用:
- 新值能立即同步到主内存,每次使用前从主内存刷新。保证多线程操作时的可见性。
- 避免指令重排序。
原子性
可见性
- volitaile
- synchronized 利用lock和unlock
- final初始化时就确定了。
有序性
- volitaile和sychronized
7.3现行并发原则
- 同一线程内前面的操作一定先行并发生与后面的。
- unlock一定线性发生于后面对同一个锁的lock
- volatile变量的写操作先行发生于后面对这个变量的读
- 线程启动
- 线程终止前运行完该线程所有代码
- interrupt()先行发生于检测到的中断事件发生
- 对象初始化发生于finalize()方法之前
- 传递性
7.4线程调度
线程调度分为两种:协同式,抢占式
Java通过映射到系统的原生线程来实现线程调度和优先级分配
线程的状态转换
- 新建
- 运行start()
- 阻塞 同步的时候
- 无线等待 wait()join()
- 限期等待 sleep()设置了事件的timeout的wait()join()
- 结束 跑完了
8线程安全
8.1线程安全的实现方法
- 互斥同步:sychronized,J.U.C下面的ReentrantLock(多个condition,公平锁,等待中断)
- 非阻塞同步,CAS根据类似版本号。J.U.C包里面的compareAndSet()和copareAndSwapLong()
- ThreadLoacal类,为每一个线程提供一个单独的变量副本
9锁优化
9.1适应性自旋锁
主要目的是消除,挂起然后重新启动的时间。
自适应是动态的判断自旋的循环次数,减少CPU自旋的时间开销
9.2锁消除
如果堆上的所有数据都不会被其他线程访问到,那就可以当作栈上的数据对待不用加锁。
字符串想加:s1+s2+s3;
9.3锁粗化
减少互斥操作的开销,多个小的锁合并成一个大的锁
9.4轻量级锁
- 加锁:检查对象头存储标志位是否为01,加锁时将栈帧中的锁记录空间存储目前对象的对象头拷贝,然后通过CAS操作将对象头更改为指向锁记录的指针。并且标志位变为00
- 失败:检查对象是否指向栈帧。不,则将锁标志位更改为10变为重量级锁,然后线程挂起。
- 解锁:CAS进行替换如果替换成功则完成,否则,释放锁还需唤醒挂起线程
9.5偏向锁
在执行过程中一直保持无锁状态知道有其他线程介入。
标志位设置为01,将对象头记录为获取对象线程的ID,同一个线程进入对象不作任何同步操作。其他线程进入则根据锁定状态,如果未锁定撤销偏向恢复01,或者进入轻量级锁(00)