敖丙思维导图系列目录
这些知识整理都是自己查阅帅丙资料(当然还有其他渠道)加以总结滴~ 每周都会更新知识进去。
如有不全或错误还请大家在评论中指出~
- 敖丙思维导图-集合
- 敖丙思维导图-多线程之synchronized\ThreadLocal\Lock\Volatitle\线程池
- 敖丙思维导图-JVM知识整理
- 敖丙思维导图-Spring
- 敖丙思维导图-Redis
- 敖丙思维导图-RocketMQ+Zookeeper
- 敖丙思维导图-Mysql数据库
本文章目录
JVM内存模型
以Sun HotSpot虚拟机为例。Java内存模型(Java Memory Model ,JMM)就是一种符合内存模型规范的,屏蔽了各种硬件和操作系统的访问差异的,保证了Java程序在各种平台下对内存的访问都能保证效果一致的机制及规范。
其中方法区和堆是所有线程共享的,栈,本地方法栈和程序虚拟机则为线程私有的(编译时确定所需内存大小)。
1. 程序计数器(为了线程切换可以恢复到正确执行位置)
如果线程执行的是个java方法,那么计数器记录虚拟机字节码指令的地址。如果为native【底层方法】,那么计数器为空。这块内存区域是虚拟机规范中唯一没有OutOfMemoryError
的区域,不需要进行 GC
。
2. 虚拟机栈(基本类型的变量+对象句柄)
每个方法被执行的时候都会创建一个栈帧
用于存储局部变量表,操作栈,动态链接,方法出口
等信息。每一个方法被调用的过程就对应一个栈帧在虚拟机栈中从入栈到出栈的过程。
两种error:
- 线程请求的栈深度大于虚拟机允许的栈深度,将抛出
StackOverflowError
。 - 虚拟机栈空间可以动态扩展,当动态扩展是无法申请到足够的空间时,抛出
OutOfMemory
异常。
引用变量是普通变量,定义时在栈中分配内存
3. 本地方法栈
虚拟机栈执行的是Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的native方法服务。
在HotSopt虚拟机中直接就把本地方法栈和Java栈合二为一。会抛出StackOverflowError+OutOfMemory
栈中主要存放一些基本类型的变量(int, short, long, byte, float, double, boolean, char)和对象句柄。
栈有一个很重要的特殊性,就是存在栈中的数据可以共享。
4. 堆 (new创建的对象和数组)
堆是java虚拟机管理内存最大
的一块内存区域,因为堆存放的对象是线程共享的,所以多线程的时候也需要同步机制。它的目的是存放对象实例。同时它也是GC所管理的主要区域,因此常被称为GC堆,又由于现在收集器常使用分代算法,Java堆中还可以细分为新生代和老年代。
- 能按扩展实现(通过设置 -Xmx和-Xms),如果堆中没有内存内存完成实例分配,而且堆无法扩展将报OOM错误(
OutOfMemoryError
)
为了保证对象的内存分配过程中的线程安全性,HotSpot虚拟机提供了一种叫做TLAB(Thread Local Allocation Buffer)的技术。堆是线程共享的内存区域”这句话并不完全正确,因为TLAB是堆内存的一部分,他在读取上确实是线程共享的,但是在内存分配上,是线程独享的。
堆Heap内存用于存放由new创建的对象和数组。
Java中的对象都是在堆上分配的吗?Java中的对象不一定是在堆上分配的,因为JVM通过逃逸分析,能够分析出一个新对象的使用范围,并以此确定是否要将这个对象分配到堆上。
即时编译判断对象是否逃逸的依据:一种是对象是否被存入堆中(静态字段或者堆中对象的实例字段),另一种就是对象是否被传入未知代码。
并不是所有的对象和数组,都是在堆上进行分配的,由于即时编译的存在,如果JVM发现某些对象没有逃逸出方法,就很有可能被优化成在栈上分配。
5. 方法区(规范)
用于存储已被虚拟机加载的类信息、常量、静态变量,如static修饰的变量加载类的时候就被加载到方法区中。在JDK1.8中,使用元空间
代替永久代来实现方法区(元空间并不在虚拟机中,而是使用本地内存),元数据空间并不在虚拟机中,而是使用本地内存(不需要进行 GC)
。但会出现OutOfMemoryError
JVM主内存与工作内存
主内存主要包括本地方法区和堆。每个线程都有一个工作内存,工作内存中主要包括两个部分,一个是属于该线程私有的栈和对主存部分变量拷贝的寄存器(包括程序计数器PC和cup工作的高速缓存区)。
1.所有的变量都存储在主内存中(虚拟机内存的一部分),对于所有线程都是共享的。
2.每条线程都有自己的工作内存,工作内存中保存的是主存中某些变量的拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的变量。
3.线程之间无法直接访问对方的工作内存中的变量,线程间变量的传递均需要通过主内存来完成。
JVM参数分类
- 标准参数,即在JVM的各个版本中基本不变的,相对比较稳定的参数
-help
-version -showversion
- X参数,非标准化参数,变化比较小的参数
-Xint:解释执行
-Xcomp:第一次使用就编译成本地代码
-Xmixed:混合模式,JVM自己来决定是否编译成本地代码,默认使用的就是混合模式
- XX参数,特点是非标准化参数,相对不稳定,主要用于JVM调优和Debug
1、Boolean类型
格式:-XX[+-]<name>其中+-表示启用或者禁用name属性
比如:-XX:+UseConcMarkSweepGC表示启用CMS垃圾收集器,-XX:+UseG1GC表示启用G1垃圾收集器
2、非Boolean类型
格式:-XX:<name>=<value>表示name属性的值是value
比如:-XX:MaxGCPauseMillis=500表示GC的最大停顿时间是500毫秒,-XX:GCTimeRatio=19
注意:-Xmx和-Xms表示设置JVM的最大内存和最小内存,它们不是X参数,而是XX参数,-Xmx等价于-XX:MaxHeapSize,-Xms等价于-XX:InitialHeapSize;-Xss设置堆栈,也是XX参数,等价于-XX:ThreadStackSize
类加载过程
包括以下 7 个阶段:
- 加载(Loading)
通过类的完全限定名称获取定义该类的二进制字节流。
将该字节流表示的静态存储结构转换为方法区
的运行时存储结构。
在内存中生成一个代表该类的 Class 对象,作为方法区中该类各种数据的访问入口 - 验证(Verification)
Class 文件的字节流中包含的信息符合当前虚拟机的要求
- 准备(Preparation)
准备阶段为类静态变量分配内存并设置初始值
,使用的是方法区的内存。
初始值一般为 0 值,例如下面的类变量 value 被初始化为 0 而不是 123。
public static int value=123; //在准备阶段 value 初始值为 0 。在初始化阶段才会变为 123 。
但是加上final后就会初始化为123
- 解析(Resolution)
将常量池的符号引用替换为直接引用的过程。 - 初始化(Initialization)
执行类构造器
。在准备阶段,类变量已经赋过一次系统要求的初始值,而在初始化阶段,通过程序制定的主观计划去初始化类变量和其它资源。 - 使用(Using)
- 卸载(Unloading)
类加载的主要步骤
• 装载。根据查找路径找到相应的 class 文件,然后导入。
• 链接。链接又可分为 3 个小步:
- 检查,检查待加载的 class 文件的正确性。
- 准备,给类中的静态变量分配存储空间。
- 解析,将符号引用转换为直接引用(这一步可选)
• 初始化。对静态变量和静态代码块执行初始化工作。
类加载器分类
- 启动类加载器(
Bootstrap ClassLoader
)此类加载器负责加载 Java 核心类库,无法被 Java 程序直接引用。
将存放在 <JRE_HOME>\lib 目录中的,或者被 -Xbootclasspath 参数所指定的路径中的,并且是虚拟机识别的(仅按照文件名识别,如 rt.jar,名字不符合的类库即使放在 lib 目录中也不会被加载)类库加载到虚拟机内存中。启动类加载器无法被 Java 程序直接引用,用户在编写自定义类加载器时,如果需要把加载请求委