一、Java内存
1.1 运行时数据区域
程序计数器:线程私有,可以看作是当前线程所执行的字节码行号指示器。类似于寄存器IP
虚拟机栈|本地方法栈:线程私有,每个方法执行的同时会创建一个栈帧入栈。栈帧包括:局部变量表、操作数栈(类似寄存器功能)、方法出口等信息
方法区:共享内存区域,存放类信息、常量、静态变量、即时编译器编译的代码等。(1.8使用NativeMem实现;1.7字符串常量池已经从方法区移到堆的中,YGC会扫描StringTable但不会回收此处如果过大可能导致YGC时间过长,FGC和CMSGC会回收)
Java堆:存放对象的共享内存区域
Java运行时常量池:是方法区的一部分,Class文件中常量池用于存放编译时的字面量和符号引用等信息在类加载后进入到运行时常量池,当然并非只有预置入Class的常量池中才能进入常量池运行期间也会有常量加入其中,如:String.intern。
直接内存:NIO、栈会用到,大小受机器总内存和寻址空间限制
1.2 对象的内存布局
1.2.1 对象的定位
1.3 内存溢出
1.堆内存溢出:申请对象空间不足 【OutOfMemoryError提示->Java Heap Space】【复现:创建n多大对象不释放】
2.栈溢出:
超过栈最大深度:【StackOverFlowError】【复现:递归】
理论上动态扩展栈时申请不到空间也会【OutOfMemoryError】【复现:书上说未能复现】
3.方法区和运行时常量池溢出:【OutOfMemoryError提示->PermGen Space】【复现:String.intern或cglib创建大量类】
4.本机内存溢出:【OutOfMemoryError无特殊提示】【复现:使用Unsafe】
1.4 垃圾收集器
1.可达性分析:从GcRoots作为起点开始向下搜索调用链路来分析对象是否可达。
2.GcRoots包括:(HotSpot采用OopMap数据结构优化)
虚拟机栈(如栈帧的本地变量表)中引用的对象
方法区中静态属性引用的对象
方法区中常量引用的对象
本地方法栈中引用的对象
3.回收简介:
1)如果分析之后发现不可达,是否需要执行finalize()方法(没覆盖该方法或者已经调用过不需要执行)。
2)有必要执行的放入F-Queue队列中,虚拟机会有一个低优先级的Finalizer线程去执行这个方法(但不保证等待结束防止方法耗时太长耽误垃圾回收)
3)稍后对F-Queue队列中对象重新标记如果还是不可达那么拜拜吧没啥说的
4.垃圾回收基本类型:
标记-清除:内存碎片
复制:浪费空间
标记-整理:复制的过程性能比较低
5.回收方法区:符合一下条件的类可以被回收,可以加入是否回收的JVM参数
符合回收条件的类:
不存在任何该类的实例
加载该类的ClassLoader已经被回收
该类对应的Class对象没有任何引用地方
6.HotSpot算法:
枚举根节点:在可达性分析时要保证一致性所以要发生STW停顿,所有线程会在安全点或者安全区域停下自己手头的工作。
7.垃圾回收器(关注点不同):
ParNew(ygc)-【复制】-:复制,多线程执行垃圾回收缩短stw,能和CMS搭配
ParallelScavenge(ygc)-【复制】-【吞吐量优先】
ParallelOld-【标记-整理】-【吞吐量优先】:多线程执行垃圾回收缩短stw
CMS-【标记-清除】-【停顿敏感】:分为多个阶段,多个并发阶段和stw阶段,极大减少stw时间。缺点对CPU资源敏感,容易造成碎片,可以设置执行N次标记-清除在执行一次标记-整理,但是标记-整理部分过程无法并发很耗时呢!!!
G1-【屌】-【停顿敏感】:可预测停顿时间,强大!用在哪都行
二、JVM工具
jps:查看java进程,-v查看启动参数
jstat:查看虚拟机统计信息,gc情况、类加载情况等
jinfo:查看和调整虚拟机配置信息,-flag可查看默认值
jmap:生成dump
jhat:分析dump,mat也可以分析
jstack:查看线程快找 -F 强制输出
三、类加载
1.什么情况下初始化一个类:有且只有!!!
遇到new、getstatic、putstatic、invokestatic这四条指令
对类进行反射调用
初始化一个类发现父类没有初始化
虚拟机启动时指定的主类,main
jdk1.7 MethodHandle实例最后的解析结果REF_getStatic,REF_putStatic,REF_invokeStatic的方法具柄
2.加载过程
加载:读取二进制流并转为运行时数据结构,生成一个Class对象作为方法区这个类各种数据的访问入口
验证:验证文件格式、元数据、字节码、符号引用等
准备:为类变量分配内存并设置【初始值如:0,null,false等】常量可以直接设置为常量值【classLoader.loadClass】
解析:将常量池的符号引用解析为直接引用【JVM规范要求在执行anewarray checkcast getfield getstatic instanceof invokedynamic invokeinterface invokespecial invokestatic invokevirtual ldc ldc_w multianewarray new putfield putstatic 16个字节码之前,先对他们所使用的富豪引用进行解析】【classLoader.loadClass("className", true)】
初始化:执行<clinit>会加被加锁保证多线程正确执行。另外类的初始化不会导致父接口初始化,接口初始化也不会导致父接口初始化【Class.forName()】
3.类加载器类型
启动类加载器:C++实现(HotSpot中)是虚拟机的一部分,加载<JAVA_HOME>/lib或者-Xbootclasspath指定路径的,可以被jvm识别的文件
扩展类加载器:java实现,加载<JAVA_HOME>/lib/ext或java.ext.dirs系统变量所指定的路径
应用程序类加载器:java实现,加载用户类路径
4.双亲委派
过程:一个类加载器收到加载类的请求自己先不处理而是先委派给父类加载(一层层向上传递),如果父类加载不到自己才加载。保证了Class的唯一性
5.破坏双亲委派
java thread class loader :允许父类加载器请求子类去加载一个类。(用于解决基础类中回调用户代码会出现该情况)
OSGi:
例如:tomcat热部署->创建自定义classloader来破坏这样的结构,指定什么类委派给父加载器加载什么类不委派。
参考:https://www.jianshu.com/p/252e27863822
四、虚拟机
1.重载:在虚拟机内的方法签名不同区别调用方法
2.重写:基本步骤->按照继承关系逐层向上查找要动用的method
虚拟机实现(虚拟机动态分派):基于性能考虑虚拟机采用在方法区为类建立一个虚方法表进行优化和一些其他优化手段
虚方法表存放着各个方法的实际入口。
3.动态语言支持
MethodHandle:代码可以实现虚拟机的分派逻辑 例子
invokedynamic指令
五、并发
1.java内存模型
2.线程安全实现方法:
互斥同步:Lock,synchronized,QAS
非阻塞同步:CAS,
不变应万变:可重入代码,不变性,线程本地存储
3.锁优化
锁膨胀:偏向锁->轻量级锁->重量级锁
4.并发工具
Semaphore信号量
CountDownLatch闭锁
CyclicBarrier栅栏
AQS
Lock
。。。