JVM
2021-01-15 10:11:16 1 举报
AI智能生成
Java虚拟机JVM知识全网最全思维导图
作者其他创作
大纲/内容
JVM内存区
运行时数据区
子主题
JVM内存区域
子主题
JVM 内存区域主要分为线程私有区域【程序计数器、虚拟机栈、本地方法区】、线程共享区域【 JAVA 堆、方法区】、直接内存。
线程私有数据区域生命周期与线程相同, 依赖用户线程的启动/结束 而 创建/销毁(在Hotspot VM内
线程共享区域随虚拟机的启动/关闭而创建/销毁。
直接内存
NIO提供了基于Channel与Buffer的IO方式, 它可以使用Native函数库直接分配堆外内存, 然后使用DirectByteBuffer对象作为这块内存的引用进行操作(详见: Java I/O 扩展), 这样就避免了在Java堆和Native堆中来回复制数据
子主题
程序计数器
当前线程所执行的字节码的行号指示器
虚拟机栈
描述方法执行的内存模型
子主题
本地方法区
为Native方法服务,和虚拟机栈类似
堆
线程共享区,对象和数组
分为新生代与老年代
方法区/永久代
类信息、常量、静态变量、JNI代码
运行时常量池
各种字面量和符号引用
运行时内存
子主题
新生代
Minor GC
复制算法
复制->清空->互换
老年代
Major GC
标记清除算法
永久代
类与元数据的信息
垃圾回收与算法
子主题
确定垃圾
引用计数法
一个对象如果没有任何与之关联的引用,即他们的引用计数都为0,则说明对象不太可能再被用到,那么这个对象就是可回收对象。
可达性分析
GC Roots到到一个对象之间没有可达路径,则称不可达
不可达对象经过两次标记仍是可回收对象,则被回收
回收算法
标记清除算法
两个阶段,标注和清除
最大的问题
是内存碎片化
复制算法
将内存划分为相同大小的两块。
每次只使用其中一块,当这一块内存满后将尚存活的对象复制到另一块上去
最大的问题
是可用内存被压缩到了原本的一半。Copy效率低。
标记整理算法
标记后不是清理对象,而是将存活对象移向内存的一端。然后清除端边界外的对象。
分代收集算法
根据不同区域选择不同的算法。
新生代与复制算法
8:1:1
一般将新生代划分为一块较大的Eden空间和两个较小的Survivor空间(From Space, To Space),每次使用Eden空间和其中的一块Survivor空间,当进行回收时,将该两块空间中还存活的对象复制到另一块Survivor空间中。
老年代与标记整理算法
子主题
引用类型
强
把一个对象赋给一个引用变量
软
要用 SoftReference 类
当系统内存空间不足时它会被回收。软引用通常用在对内存敏感的程序中。
弱
要用 WeakReference 类来实现
只要垃圾回收机制一运行都会回收
虚
PhantomReference 类来实现,它不能单独使用,必须和引用队列联合使用。
作用是跟踪对象被垃圾回收的状态。
分代收集算法vs分区收集算法
分代算法
新生代-复制算法
老年代-标记整理
因为对象存活率高, 必须采用“标记—清理”或“标记—整理”
分区算法
将整个堆空间划分为连续的不同小区间, 每个小区间独立使用, 独立回收. 这样做的好处是可以控制一次回收多少个小区间
GC 垃圾收集器
Serial 垃圾收集器(单线程、复制算法)
Client 模式下默认的新生代垃圾收集器。
ParNew 垃圾收集器(Serial多线程版本)
Server 模式下新生代的默认垃圾收集器
Parallel Scavenge 收集器(多线程复制算法、高效)
关注的是程序达到一个可控制的吞吐量,年轻代
与ParNew相似,自适应调节策略是重要区别
Serial Old 收集器(单线程、标记整理算法 )
Client 默认的 java 虚拟机默认的年老代垃圾收集器
Parallel Old 收集器(多线程、标记整理算法)
Parallel Old 是PS老年版
CMS 收集器(多线程标记清除算法)
年老代垃圾收集器,其最主要目标是获取最短垃圾回收停顿时间,和其他年老代使用标记-整理算法不同,它使用多线程的标记-清除算法。
初始标记
并发标记
重新标记
并发清除
G1 收集器
两个最突出的改进是:1. 基于标记-整理算法,不产生内存碎片。2. 可以非常精确控制停顿时间,在不牺牲吞吐量前提下,实现低停顿垃圾回收。G1 收集器避免全区域垃圾收集,它把堆内存划分为大小固定的几个独立区域,
子主题
JAVA IO/NIO
Block IO
data = socket.read();
Non-Block IO
用户线程需要不断地询问内核数据是否就绪,也就说非阻塞 IO不会交出 CPU,而会一直占用 CPU。
多路复用IO
一个线程不断去轮询多个 socket 的状态,只有当 socket 真正有读写事件时,才真正调用实际的 IO 读写操作
多路复用 IO 比较适合连接数比较多的情况。
非阻塞 IO 中,不断地询问 socket 状态时通过用户线程去进行的,而在多路复用 IO 中,轮询每个 socket 状态是内核在进行的
信号驱动 IO 模型
用户线程发起一个 IO 请求操作,会给对应的 socket 注册一个信号函数,然后用户线程会继续执行,当内核数据就绪时会发送一个信号给用户线程,用户线程接收到信号之后,便在信号函数中调用 IO 读写操作来进行实际的 IO 请求操作
异步IO
当用户线程发起 read 操作之后,立刻就可以开始去做其它的事。
只需要先发起一个请求,当接收内核返回的成功信号时表示 IO 操作已经完成,可以直接去使用数据了。
JAVA NIO
NIO 主要有三大核心部分:Channel(通道),Buffer(缓冲区), Selector。
传统 IO 基于字节流和字符流进行操作,而 NIO 基于 Channel 和 Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。
IO 是面向流的,NIO 是面向缓冲区的。
NIO 缓冲区
数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。
非阻塞
直至数据变的可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。
Channel
Stream 是单向的,而 Channel 是双向的
Buffer
缓冲区,实际上是一个容器,是一个连续数组。Channel 提供从文件、网络读取数据的渠道,但是读取或写入的数据都必须经由 Buffer。
Selector
Selector 能够检测多个注册的通道上是否有事件发生,如果有事件发生,便获取事件然后针对每个事件进行相应的响应处理
JVM类加载机制
类加载机制
加载,验证,准备,解析,初始化
加载
这个阶段会在内存中生成一个代表这个类的 java.lang.Class 对象,作为方法区这个类的各种数据的入口。
验证
确保 Class 文件的字节流中包含的信息是否符合当前虚拟机的要求
准备
在方法区中分配这些变量所使用的内存空间
实际上变量 v 在准备阶段过后的初始值为 0 而不是 8080,除非final变量
解析
虚拟机将常量池中的符号引用替换为直接引用的过程
符号引用就是 class 文件中的:1. CONSTANT_Class_info2. CONSTANT_Field_info3. CONSTANT_Method_info等类型的常量。
符号引用
引用的目标并不一定要已经加载到内存中。各种虚拟机实现的内存布局可以各不相同
直接引用
指向目标的指针,相对偏移量或是一个能间接定位到目标的句柄
那引用的目标必定已经在内存中存在
初始化
真正执行类中定义的 Java 程序代码。
类构造器<client>
执行类构造器<client>方法的过程。
如果一个类中没有对静态变量赋值也没有静态语句块,那么编译器可以不为这个类生成<client>()方法。
以下几种情况不会执行类初始化:
子主题
类加载器
启动类加载器(Bootstrap ClassLoader)
加载 JAVA_HOME\lib 目录中的,或通过-Xbootclasspath 参数指定路径中的
扩展类加载器(Extension ClassLoader)
加载 JAVA_HOME\lib\ext 目录中的,或通过 java.ext.dirs 系统变量指定路径中的类库。
应用程序类加载器(Application ClassLoader)
加载用户路径(classpath)上的类库。JVM 通过双亲委派模型进行类的加载,当然我们也可以通过继承java.lang.ClassLoader实现自定义的类加载器。
双亲委派
当一个类收到了类加载请求,他首先不会尝试自己去加载这个类,而是把这个请求委派给父类去完成,每一个层次类加载器都是如此,因此所有的加载请求都应该传送到启动类加载其中,只有当父类加载器反馈自己无法完成这个请求的时候(在它的加载路径下没有找到所需加载的Class),子类加载器才会尝试自己去加载。
最终都是委托给顶层的启动类加载器进行加载,这样就保证了使用不同的类加载器最终得到的都是同样一个 Object 对象。
子主题
OSGI(动态模型系统)
OSGi(Open Service Gateway Initiative),是面向 Java 的动态模型系统,是 Java 动态化模块化系统的一系列规范。
动态改变构造
OSGi 服务平台提供在多种网络设备上无需重启的动态改变构造的功能。
模块化编程与热插拔
实现模块级的热插拔功能
0 条评论
下一页