一、JVM = 类加载器(classloader) + 执行引擎(execution engine) + 运行时数据区域(runtime data area)
二、运行时数据区
2.1 划分
程序计数器、native栈、java栈、堆区(gc区)、方法区
2.2 各区作用
2.2.1 程序计数器
线程私有
可以看作当前线程所执行字节码的信号指示器,字节码解释器会改变线程中程序计数器的值,指示代码下一步执行的字节码指令,如:分支、循环、跳转、异常处理、线程切换等。
当前线程执行Java方法时,程序计数器会记录当前程序执行的虚拟机字节码指令的地址;若执行的是native方法,程序计数器的值为空null
不会有异常
2.2.2 java栈
java栈为每个线程自身所有。
java的每个方法在执行的时候会涉及栈帧的出入栈,栈帧存储这个方法的局部变量表、操作栈、动态链接、方法返回地址等信息。
局部变量表存储了在编译期间就可知的java基本数据类型的值、对象的引用和returnAddress。
局部变量表所需要的内存空间在编译期间就分配完成,该方法需要的帧分配空间大小是确定的,在运行期间是不会改变的。
可能会有StackOverflow异常和OutofMemory异常
2.2.3 native栈
功能和Java栈类似,不同的是执行本地native方法,也是线程私有,也可能会有StackOverflow异常和OutofMemory异常
2.2.4 方法区
各线程共享区域
功能是存储已经被加载的类信息、静态变量、常量值、即时编译器编译后的代码,hotSpot说的永久区就是这块区。
gc算法是标记-清理或者标记-整理算法
可能会有OutofMemory异常
2.2.5 堆
线程共享区域
对象的产生都在这个区域,这个区域划分为eden fromsurvivor tosurvivor 三块,大小比例为8:1:1,垃圾回收操作集中在这一块区域,垃圾回收算法是复制算法。
可能会有OutofMemory异常
三、垃圾回收机制
3.1 确认哪些对象要回收
3.1.1 引用计数法
该方法的实现是给每个对象加一个引用计数器,当jvm中有地方引用该对象,那么引用计数器的值加1;当引用失效时,引用计数器的值就减1。当引用计数器的值为0,那么这个对象就是回收的对象了。
缺点:无法解决互相引用的情况。
3.1.2 可达性算法
从GcRoot对象作为起点,从这些节点向下搜索,搜索走过的路经称为引用链,当一个对象到GcRoot对象没有任何引用链时,就意味着它可以被回收了。
GcRoot对象:
虚拟机栈中引用的对象
native栈中引用的对象
方法区中常量引用对象
方法区中静态变量引用对象
3.2 怎么回收(垃圾收集算法)
3.2.1 标记-清除法
顾名思义,标记处要清除的对象,然后清除
缺点:
从效率角度来说,两个过程的执行效率都不高
从空间角度来说,会产生大量不连续的内存碎片,导致程序在运行期间需要分配大对象的时候找不到合适的内存空间而引发gc操作
3.2.2 复制法
将可用内存空间分成两块,程序运行期间,每次只使用其中一块,当发生gc操作时,将不需要清除的对象移动至另一块内存空间,然后清除当前使用的内存空间。
实际虚拟机中不是平均分成两块,而是8:1:1的比例划分。
3.2.3 标记-整理法
当发生gc操作时,将不需要清除的对象移动至内存空间的一侧,然后直接清除边界以外的内存。
3.2.4 分代收集算法
大量对象死去,少量对象存活的内存去,比如堆区,用复制算法。
对象存活率高,采用标记-清理算法或者标记-整理算法。
3.3 垃圾收集器(垃圾回收算法的实现的地方)
3.3.1 Serial
采用复制算法的单线程收集器,当该收集器进行进行垃圾回收时必须暂停其他线程的所有工作,直到它的收集工作结束。
3.3.2 ParNew
使用多线程的Serial收集器。
3.3.3 Parallel Scavenge
吞吐量优先收集器,并行的多线程收集器。关注吞吐量(吞吐量=运行用户代码时间/(运行用户代码时间+垃圾回收时间))。
3.3.4 Serial Old(MSC)
老年代的单线程收集器,利用“标记-整理算法”
3.3.5 Parallel Old
老年代中,使用多线程的的Serial Old
3.3.6 CMS
老年代的垃圾收集器。
收集过程有必要了解:
1)、初试标记,标记GCRoot能直接关联到的对象,时间很短
2)、并发标记,进行可达性分析,GCRoot Tracing过程,时间很长
3)、重新标记,修正并发标记期间,用户操作产生的,新的需要清除的对象,时间长
4)、并发清除,回收内存空间,时间长。
其中并发标记和并发清除的过程耗时很长,但是可以与用户线程并发执行。
3.3.7 G1
1)并发和并行。使用多个CPU来缩短STW时间,与用户线程并发执行
2)可预测的停顿
3)空间整合。基于标记-整理算法,无内存碎片。
四、类加载机制
4.1 过程
加载 ->验证 -> 验证->准备 ->解析 ->初始化 ->使用 ->卸载
类加载的五个过程:加载、验证、准备、解析、初始化。
加载:
在加载阶段,虚拟机主要完成三件事:
1.通过一个类的全限定名来获取定义此类的二进制字节流。
2.将这个字节流所代表的静态存储结构转化为方法区域的运行时数据结构。
3.在Java堆中生成一个代表这个类的java.lang.Class对象,作为方法区域数据的访问入口。
验证:
验证阶段作用是保证Class文件的字节流包含的信息符合JVM规范,不会给JVM造成危害。如果验证失败,就会抛出一个java.lang.VerifyError异常或其子类异常。验证过程分为四个阶段:
1.文件格式验证:验证字节流文件是否符合Class文件格式的规范,并且能被当前虚拟机正确的处理。
2.元数据验证:是对字节码描述的信息进行语义分析,以保证其描述的信息符合Java语言的规范。
3.字节码验证:主要是进行数据流和控制流的分析,保证被校验类的方法在运行时不会危害虚拟机。
4.符号引用验证:符号引用验证发生在虚拟机将符号引用转化为直接引用的时候,这个转化动作将在解析阶段中发生。
准备:
准备阶段为变量分配内存并设置类变量的初始化。在这个阶段分配的仅为类的变量(static修饰的变量),而不包括类的实例变量。对已非final的变量,JVM会将其设置成“零值”,而不是其赋值语句的值:
pirvate static int size = 12;
那么在这个阶段,size的值为0,而不是12。 final修饰的类变量将会赋值成真实的值。
解析:
解析过程是将常量池内的符号引用替换成直接引用。主要包括四种类型引用的解析。类或接口的解析、字段解析、方法解析、接口方法解析。
初始化:
在准备阶段,类变量已经经过一次初始化了,在这个阶段,则是根据程序员通过程序制定的计划去初始化类的变量和其他资源。这些资源有static{}块,构造函数,父类的初始化等。
至于使用和卸载阶段阶段,这里不再过多说明,使用过程就是根据程序定义的行为执行,卸载由GC完成。
4.2 双亲委派机制
Bootstrap ClassLoader
Extention ClassLoader
Application ClassLoader
Custom ClassLoader
未完待续……
网友评论