第一部分:java的发展史,jvm的发展史
- java的出现:Green Project,开发在电子产品上运行的程序架构 
- jvm迭代:Classic Vm -> Exact Vm ,HotSpot Vm (解释器跟编译器的混合执行) 
- jdk跟jre:java开发工具(java语言,java api,jvm),java运行环境(java se api,jvm) 
- java的优势:结构严谨,面向对象,内存管理与指针越界管理,热点代码检测,运行时编译及优化,一次编写,到处运行,庞大而完整的生态 
联想:
- java9,10,11,12,13新特性:模块化,集合工程,各种增强,局部类型推断var关键字,加入json解析api,httpclient等
问题:
- 为啥解释器跟编译器混合执行就牛逼了?
第二部分:jvm的内存管理
主要内容:
- jvm的内存划分 - 程序计数器:记录指令执行 
- 虚拟机栈: - -Xss 
- java方法执行时局部变量与栈帧的分配,请求的栈深度过大-StackOverFlowError,无法再申请内存分配栈帧-OutOfMemoryError 
 
- 本地方法栈: - -Xos 
- 本地方法执行时的内存分配,再hotsopt中,不区分本地方法栈与虚拟机栈 
 
- java堆: - -Xms 最小大小,-Xmn最大大小 
- 常规对象分配的地方 
- OutOfMemoryError java heap space 
 
- 方法区: - -XX:MaxPermSize 
- 类元数据+运行时常量池 
- OutOfMemoryError perm space 
 
- 直接内存区: - 在虚拟机外分配使用的内存,常见于NIO中使用,避免内存复制
 
 
- 对象的创建 - 对象内存的分配 - 指针碰撞,计算对象大小,划分内存,要考虑同步 
- TLAB,在线程自己的内部空间里面分配 
- 需要结合GC方式 
 
- 对象的内存布局 - 对象头:hash code,分代年龄,锁状态,类型指针(确定对象是哪个类的实例) 
- 对象实例数据 
- 对齐空间 
 
 
- 垃圾回收 - 对象回收: - 先判断对象是否已死: - 引用计数法,无法识别两个死亡对象互相引用的情况 
- 可达性分析,通过枚举Gc Roots,如:常量引用,静态变量引用,栈上的引用 
 
- 回收时机:安全点 
 
- 回收算法 - 复制算法:内存消耗大 
- 分代整理:基于复制算法的思路 
- 标记整理: 
- 标记清除:内存碎片 
 
- 回收器 - 新生代 - serial:stop the world 
- ParNew:stop the world ,回收多线程并行 
- Parallel Scavenge:类似于ParNew 更注重于控制吞吐量 
 
- 老年代 - serial old:stop the world 
- parallel old:stop the world ,回收多线程并行 
- cms: - 标记:stop the world 
- 并发标记:并发 
- 重标记:stop the world 
- 并发回收:并发,回收时,用户进程也在进行,所以会产生浮动垃圾。所以内存上需要留有余地 
 
 
- G1 能适用整堆上的垃圾回收 
 
 
- 故障排查,性能分析工具 - bin目录下的自带工具 
- jconsole,visualvm 
 
- 调优案例总结 - 高性能硬件 - 采用逻辑集群+负载均衡的方式来发挥高性能硬件的优点 
- 回收过大的内存区域,会导致GC时间过长 
 
- 集群通讯导致大对象累积在内存中 
- 使用NIO。DirectMemory的回收直到full gc才会顺便回收,当堆外内存无法分配也会导致OutOfMemory 
- Runtime.exec()会创建系统进程,要慎用 
- 依赖其他的远程服务太过耗时,导致线程,Socket挂起,直到jvm崩溃 
- 数据结构导致内存翻倍,比如long 占用8B,Long 占用24B 
- 桌面程序最小化之后,工作内存转移到磁盘,恢复的时候可能导致不正常的GC 
 
联想:
- jdk8新增MetaSpace,将类元数据从方法区中移动到meta space 
问题:
第三部分:
主要内容:
- 平台无关性:java通过虚拟机+字节码文件实现了平台无关性,jvm不仅能识别java编译器生成的字节码文件,还有很多语言在字节码文件的基础上,实现了自己的编译器,把自己的语言规范编译成字节码文件交给jvm执行。字节码文件有自己一系列的定义,比如,文件开头1-4字节为魔数,接下来的5-8字节为版本号,9-10字节代表常量池,同时定义了很多结构体,跟固定的协议,来解析class文件,其中方法体的代码是通过编译成jvm 指令存放到code属性里面 
- 类加载的时机 - new,类变量的访问与操作,静态方法调用 
- 对类反射调用 
- 初始化一个类的时候,若父类没有初始化,要先触发父类的初始化 
- 虚拟机启动时,main方法所在的主类 
- 动态语言支持如MethodHandler实例解析结果的方法句柄对应的类没有初始化(类似于反射) 
 
- 类的加载过程 - 加载:将字节码的码流加载入jvm 
- 验证: - 格式验证(魔数,版本等),通过后码流便按结构进入了方法去 
- 元数据验证 
- 字节码验证(程序语义验证) 
- 符号引用验证 
 
- 准备 - 类变量的分配内存空间 
- 设置类变量初值 - static 变量 0,false,null等 
- static finnal 常量 显式的初始值 
 
 
- 解析 - 符号引用替换成直接饮用
 
- 初始化 - 执行cinit(编译器自动收集所有类变量的赋值动作跟static静态代码块),并发问题由jvm控制,多线程并发也保证只执行一次,与init不同,init方法上实例构造器
 
- 使用 
- 卸载 
 
- 类加载器 - Bootstrap ClassLoader 加载java_home下lib目录的jar 
- Extension ClassLoader 加载java_home下lib/ext目录的jar 
- 类+类加载器唯一确定一个类 
- 双亲委派模型,越是基础的类,越由上层加载器加载 
- 双亲委派模型的破坏 - jndi,jdbc等,需要上层类加载器主动要求下层的类加载器帮忙加载 
- osgi:模块化 
 
 
- 字节码执行引擎 - 栈帧 - 局部变量表 
- 操作数栈 
- 动态连接 
- 方法返回地址(恢复调用者的栈帧) 
- 附加信息 
 
- 方法调用 - 解析(方法的调用版本在运行期是不可以改变的,这类方法的调用叫解析,如:静态方法,私有方法,这些方法在类加载的解析过程中,会把符号引用替换为直接引用) 
- 分派 - 静态分派:依赖静态类型定位方法执行版本如:方法重载,在编译期就能确定 
- 动态分派:在运行期根据实际类型确定方法执行版本如:子类重写父类方法,invokevirtual指令会在实际类型中找方法 
 
 
- java的动态语言特性支持 - 动态类型语言:在运行期才会去做类型检查,所以c,c++,java本身是一门静态类型语言 
- jdk7 提供了invokedaynamic指令,invoke包 执行方法句柄参数 
 
- 基于栈的执行引擎 - 物理机大多基于寄存器做执行引擎,速度快,但与硬件耦合太紧 
- 虚拟机基于栈的架构,速度稍慢,可移植性强 
 
 
联想:
问题:
第四部分:程序编译与代码优化
主要内容:
- 编译期优化 - 语法糖 - 范型,增强for循环,自动拆装箱,
 
 
- 运行期优化 - 解释器:把字节码解释机器吗 
- 编译器:把整个方法,或者某段循环的代码,进行优化编译成机器码 
- 编译优化: - 公共子表达式消除 
- 数组边界检查消除(如果99%的情况不回越界,可以不判断越界情况直接使用,并处理越界异常,同样适用于NPE) 
- 方法内联:如果在运行时不回出现多个方法版本,可以将代码内联,减少栈分配与复原 
- 逃逸分析:如果能确定对象不回逃逸出某个范围,则可以使用栈上分配,同步消除,标量替换 
 
 
联想:
问题
第五部分:高效并发
主要内容:
- java内存模型 - 内存操作 - lock 
- unlock 
- read:从主内存read到工作内存 
- load:从工作内存load到变量副本 
- use:工作内存到执行引擎 
- assign:赋值给工作内存的变量 
- store:工作内存的变量值store到主内存 
- write:把主内存的值放入到主内存的变量中 
 
- volatile的使用 - 语义:防止指令重排,变量的可见性 
- 使用的约束:不依赖当前值或者只有单一的线程修改,不与其他的状态变量参与到不变约束 
- 变量规则(1-4 实现可见,5,6实现防止指令重排) - use 之前 必须 load 
- load 之后 必须 use 
- store 之前 必须 assign 
- assign 之后 必须 store 
- A->V :use + assign,P->V:read+write,B ->W: load+store,Q -> W: read+write 
- lock 空操作的内存屏障 
 
 
 
- 先行发生原则: - 程序次序规则:一个线程内,书写在前面的先行发生于书写在后面的 
- 管程锁定:unlock 先行发生于后面对同一个锁的lock 
- volatile:写操作先行发生于后面的读操作 
- 线程终止:线程中的所有操作先行发生于对此线程的终止检测 
- 线程中断:对线程interrupt调用先行发生于对中断的检测 
- 对象终结:对象初始化限行发生于对象finalize方法 
- 传递性:A先行发生于B,B 。。。 C 则 A。。。C 
 
- java线程 - jdk1.2之前使用用户线程实现,1.2之后使用操作系统的的原生的线程模型实现 
- 线程调度 - New 
- Ruing 
- Waiting/Timed Waiting 
- Blocked 
- Terminated 
 
 
- 线程安全与锁优化 - 自旋锁:在锁能很快释放掉的情况下,可以通过自旋在不放弃cpu的情况下,减少线程调度的开销 
- 自适应自旋:通过统计上一次的自旋情况来决定是否要自旋,还是挂起等待 
- 锁消除:局部对象的引用安全,没有逃逸等,可以使用锁消除 
- 锁粗化:多次重复对同一个锁操作,可以扩大锁范围 
- 轻量级锁:使用cas方式加锁 
- 偏向锁:在持有锁之后,没有竞争 
 
联想:
解决java中的并发问题,其实变成是捋清楚内存操作的顺序
问题: