这次还怕不懂JVM吗
Java虚拟机浅谈
本文志在简单梳理一下JVM重要的知识点,详细理解推荐书籍《深入理解Java虚拟机:JVM高级特性与最佳实践》
Java虚拟机主要包括以下几个运行时数据区域:1、程序计数器,2、java虚拟机栈,3、本地方法栈,4、java堆,5、方法区,6、运行时常量池。本文除了简单介绍以上运行时数据区域,还会引入垃圾回收的一些东西。
运行时数据区域
程序计数器
程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、异常处理等基础功能都需要依赖这个计数器来完成。
程序计数器是线程私有的内存,因为,为了线程切换后能恢复到正确的执行位置,每条线程都需要一个独立的程序计数器,各条线程之间计数器互不影响。
Java虚拟机栈
与程序计数器一样,Java虚拟机栈也是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧再虚拟机栈中入栈到出栈的过程。
本地方法栈
本地方法栈与虚拟机栈所发挥的作用类似,不同的是,虚拟机栈执行Java方法服务,而本地方法栈则为虚拟机使用到的Native方法服务。
Java堆
Java堆是Java虚拟机所管理的内存中最大的一块。java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。Java堆是垃圾收集器管理的主要区域,因此有时也被称为"GC堆"。
方法区
方法区与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
运行时常量池
运行时常量池是方法区的一部分。Class文件除了有类的版本、字段、方法、接口等信息外,还有一项信息是常量池,用于存放编译器生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。
如何判断对象是否还活着
1.引用计数法
如何判断对象是否还存活:最简单的是给对象中添加一个引用计数器,每当有个一个地方引用它,计数器就加1。当引用失效是,计数器就减1。任何时刻计数器为0的对象就是不可能再被使用的。该方法实现简单,效率也很高。但是它却很难解决对象之间相互循环引用的问题。比如对象A和对象B互相引用,导致它们的计数器都不为0,于是引用计数算法无法通知GC收集器回收它们。
2.可达性分析算法
可达性分析法,通过一系列称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时(即从GC Roots到这个对象不可达),则证明此对象是不可用的。即被判定为可回收的对象。
在Java语言中,可作为GC Roots的对象包括以下几种:
1.虚拟机栈(栈帧中的本地变量表)中引用的对象。
2.方法区中类静态属性引用的对象。
3.方法区中常量引用的对象。
4.本地方法栈中JNI(即一般说的Native方法)引用的对象。
垃圾收集算法
1.标记-清除算法
最基础的收集算法,如同它的名字一样,算法分位“标记”和“清除”两个阶段:首先标记出所需要回收的对象,在标记完成后统一回收所有被标记的对象。但是它主要不足有两个:一个是效率问题,效率较低,另一个是空间问题,标记清除后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。
2.复制算法
为了解决效率问题,复制收集算法出现了。它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次性清理掉。这样使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片的复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。只是这种算法的代价是将内存缩小为原来一般,代价有点高。
3.标记-整理算法
复制收集算法在对象存活率较高时就要进行较多的复制操作,效率会变低。更关键的是如果不想浪费50%的空间,就需要额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,所以老年代一般不能直接用这种方法。
根据老年代的特点,有了新的“标记-整理”算法。标记与“标记-清除”中的一样,但后续不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
4.分代收集算法
当前商业虚拟机都采取该算法,根据对象存活周期的不同将内存划分为几块。一般是把Java堆分位新生代和老年代,这样就可以根据各个年代的特点采用最合适的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,就选用复制算法,只需要付出少量存活对象的复制成为就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记-清除”或者“标记-整理”算法来进行回收。
垃圾收集器
1.Serial收集器。单线程收集器,工作的时候,会暂停其他所有的工作线程。
2.ParNew收集器。Serial收集器的多线程版本。
3.Parallel Scavenge收集器。一个新生代收集器,使用复制算法,并行的多线程程收集器。
4.Serial Old收集器。是Serail收集器的老年代版本,单线程收集器,使用“标记-整理”算法。
5.Parallel Old收集器。是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法。
6.CMS收集器。一种以获取最短回收停顿时间为目的的收集器。
7.G1收集器。特点:并行与并发,分代收集,空间整合,可预测的停顿。
虚拟机类加载机制
1.加载
2.验证
3.准备
4.解析
5.初始化
6.使用
7.卸载