1.JVM运行时内存分布
1.1 虚拟机栈
存储局部变量表、操作数、动态连接、方法出口等。
1.2 堆
存放所有对象实例,所有线程共享。垃圾收集器主要管理区域。
1.3 方法区
存储类型信息,常量、静态变量、即时编译器编译的代码缓存等。
2. 垃圾收集器
引用计数?NO,并不是主流。 可达性分析?Yes
2.1 分代收集
- 标记清除
标记被引用的,清除未使用的。或反过来。
缺点:执行效率不稳定;内存空间碎片化 - 标记复制
内存均分2块,使用完后把存活对象复制到另一块,清空当前块。
缺点:内存浪费严重 - 标记整理
标记存活,然后将存活对象向内存一端移动保持内存紧凑。
缺点:内存移动过程中需要暂停用户应用,造成处理延迟。
2.2 垃圾收集器对比
常见的垃圾收集器分3类,未列举JDK8以后的新收集器
- 负责堆年轻代中的内存回收
包括:Serial,ParNew,Parallel Scavenge - 负责堆老年代中的内存回收
包括:Serial Old,CMS,Parallel Old - 负责整个Java堆中的内存回收(新生代和老年代)
包括:G1
2.3 垃圾收集参数调优
各种垃圾收集器都有各自的优缺点,需要根据业务出发,进行基于垃圾回收器的性能测试,然后选择适合的。
-XX:+UseSerialGC:在新生代和老年代使用串行收集器
-XX:+UseParNewGC:在新生代使用并行收集器
-XX:+UseParallelGC :新生代使用并行回收收集器,更加关注吞吐量
-XX:+UseParallelOldGC:老年代使用并行回收收集器
-XX:ParallelGCThreads:设置用于垃圾回收的线程数
-XX:+UseConcMarkSweepGC:新生代使用并行收集器,老年代使用CMS+串行收集器
-XX:ParallelCMSThreads:设定CMS的线程数量
-XX:+UseG1GC:启用G1垃圾回收器
3. Class文件
JVM不仅仅只是Java语言,因此使用Class文件作为编译产出物。格式如下:
4. 加载
4.1 类的生命周期
4.2 类加载器
定义:通过一个类的全限定名来获取描述该类的二进制字节流
作用:将类加载到JVM
-启动类加载器
负责<JAVA_HOME>\lib目录下的类
-扩展类加载器
负责<JAVA_HOME>\lib\ext下的类
-应用程序类加载器
负责用户路径下所有类的加载
4.3 执行引擎
分派:静态分派和动态分派
1.静态类型
编译期可知,不会根据运行条件产生类型变化。如 Object o1 = new String()
2.实际类型
编译期不可知,根据运行结果产生变化。 如 Object o2 = isTrue?new String():new Int()
3.静态分派(overload)
依赖静态类型决定方法执行版本的分派动称为静态分派,因此静态分派不是由虚拟机执行,而是在编译期确定的。最广泛的的情景就是重载
注意:静态分派在拥有多个可适配重载版本的时候,会存在潜在的类型转换。
4. 动态分派(override)
在运行期进行接收者的实际类型确定,在操作数栈顶中查找对象的实际类型并判断相符。如果不符则向父类搜索和验证。这就是重写的确定过程。
5. 编译与执行
5.1 编译器分类:
-前端编译器
.java文件到.class文件
-即时编译器
字节码到机器码
-提前编译器
.java到机器码
5.2 javac编译器
属于前端编译器,包括4个过程:
- 准备过程
初始化插入式注解器 - 解析与填充过程
词法分析、语法分析、填充符号表 - 插入式注解器注解过程
以@注解形式的代码解析发生在这里 - 分析与字节码生成过程
数据流和控制流分析、自动拆装箱,解语法糖等
经过这4个过程产生字节码。
6 线程于安全
java多线程的内存模型
主内存与工作内存间存在同步的问题。
6.1 volatile关键字
保证变量对所有线程可见性;禁止指令重排优化
注意:对线程的可见性并不一定保证线程安全,只及时回写主内存,但其他线程如果变量本身计算基于该变量的值,则保证不了安全。
6.2 线程实现
1:1线程全映射内核线程
1:N一个内核线程映射所有用户线程,线程同步不由系统管理
N:M多内核线程映射所有用户线程,线程同步不由系统管理
Java的HotSpot使用1:1映射,抢占式多线程实现。
6.3 synchronized关键字
以互斥同步手段实现线程安全,在同步块前后形成monitorenter和monitorexit字节码。在进出时获取对象锁。
注意:由于java的线程是基于系统,那么在获取锁的时候唤醒其他线程会切换用户态和核心态,频繁切换会造成性能浪费。
6.4 Lock接口
在sync关键字基础上,增加了高级功能:等待可中断、公平锁、锁绑定多个条件
6.5 非阻塞同步
CAS:Compare-and-Swap
依赖于新的处理器指令,比较并交换,JDK9以后可用。