深入理解Java虚拟机
Posted pycrab
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入理解Java虚拟机相关的知识,希望对你有一定的参考价值。
Java虚拟机内存区域
Java虚拟机所管理的内存由以下五个运行时数据区域组成。
1.程序计数器
可以把程序计数器看作当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选择下一条需要执行的字节码指令。
由于JVM的多线程是通过线程轮流切换并分配处理器执行时间的方式实现的,在一个确定的时刻,一个处理器(对多核处理器来说是一个内核)只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每个线程需要一个独立的程序计数器,各线程之间程序计数器互不影响,独立存储,即程序计数器是线程私有的。
2.虚拟机栈
与程序计数器一样,Java虚拟机栈也是线程私有的,它使用栈帧来存储方法的局部变量表、操作数栈、动态连接方法和返回地址等信息。每个方法的调用和返回对应栈帧的入栈和出栈操作。
栈帧由三部分组成:
- 局部变量表:定义为一个通过下标访问的数组,存放基本类型的变量和对象引用,long和double类型占两个字节,其他类型占用一个字节,在编译期完成空间分配,即通过栈指针移动来动态调整内存空间。
- 操作数栈:定义为一个通过push和pop操作访问的数组。
- 帧数据区:解析常量池的数据,方法执行完处理方法返回,存储一个异常表供JVM查找。
3.本地方法栈
类似于虚拟机栈,虚拟机栈用于管理Java方法调用,本地方法栈用于管理本地方法调用。只是它与虚拟机有同样的权限,比如直接使用本地处理器的寄存器,或者从本地内存的堆中分配内存。
4.Java堆
在虚拟机启动时创建,被所有线程共享,用于存储对象实例及数组,也是GC的主要区域。
5.方法区
主要保存类的元数据,包括类的类型信息、常量池、静态变量、域信息、方法信息等数据,被所有线程共享。
垃圾收集算法
常见的两种垃圾标记算法分别是引用计数算法和可达性分析算法。
- 引用计数法只需为每个对象分配一个整型的计数器,在为对象赋值时检查计数器是否为0,是则可回收。
- 可达性分析算法是以根对象集合为起点,从上至下搜索与根对象集合通过引用链连接的目标对象,不被连接即不可达,被第一次标记,但是至少要经过两次标记才能被回收。如果对象没有重写finalize()方法,或者finalize()方法已经被虚拟机调用过,则被第二次标记,可被回收;否则,该对象被放置在F-Queue队列中,由一条虚拟机自动建立的低优先级线程Finalizer执行finalize()方法。
1.标记清除算法
分为标记和清除两个阶段,效率不高,且会产生内存碎片。
2.复制算法
将内存分为大小相等的两块,每次使用一块,当这一块内存不足则将存活的对象复制到另一块并回收这一块内存。效率高,不会产生内存碎片,但是需要将内存折半。
3.标记整理算法
进行标记过程后,将存活的对象移到内存一端,回收无用对象的空间。效率高于标记清除算法,且不需要牺牲一半空间。
4.分代收集算法
根据对象生命周期将Java堆区分为年轻代和老年代,年轻代又分为Eden空间,From Survivor空间和To Survivor空间。在HotSpot中,Eden空间与Survivor空间默认比例为8:1。
年轻代通常使用复制算法,速度优先;老年代通常使用标记整理算法,节省内存。
垃圾收集器
1.Serial收集器
串行回收,复制算法(Serial Old收集器是标记整理算法),STW机制。受限于单CPU。
启用后年轻代和老年代都使用串行收集器。
2.ParNew收集器
并行回收,复制算法,STW机制。在多CPU、多核心、低延迟占优势。
启用后,年轻代使用并行收集器,老年代使用串行收集器。
3.Parallel收集器
并行回收,复制算法(Parallel Old收集器是标记整理算法),STW机制。在多CPU、多核心、高吞吐量占优势。
启用后,年轻代使用并行收集器,老年代使用串行收集器。
4.CMS收集器
并行回收,标记清除算法,STW机制。低延迟是优势。
老年代垃圾收集器。
四个执行过程:
- 初始标记阶段
- 并发标记阶段
- 再次标记阶段
- 并发清除阶段
5.G1收集器
6.对象内存分配与回收策略
使用指针碰撞技术和空闲列表法(有内存碎片时使用)进行内存空间分配。
- 对象优先在Eden区分配
- 大对象直接进入老年代
- 长期存活的对象将进入老年代
JVM类加载机制
类从加载到虚拟机内存到卸载出内存经历七个生命周期阶段,分别是:加载、验证、准备、解析、初始化、使用和卸载。
- 加载:使用类加载器,通过类的全限定名获取二进制字节流,将其静态存储结构转化为方法区的运行时数据结构,在内存中生成该类的java.lang.Class对象。
- 验证:确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机。
- 准备:在方法区中为类变量分配内存并设置初始值。
- 解析:将常量池中的符号引用替换为直接引用。
- 初始化:执行java程序代码,在Java堆中初始化实例变量。
类加载器双亲委派模型
1.概念
类加载器从顶层往下依次是启动类加载器、扩展类加载器、应用程序类加载器、自定义类加载器。这种层次关系称为双亲委派模型,要求除了顶层的启动类加载器,其他的类加载器都要有组合关系复用的父类加载器。
2.工作过程
如果一个类加载器收到类加载的请求,它首先不会尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一层如是,所以直到最顶层,当父类加载器反馈无法完成加载请求时,子加载器才尝试去加载。
以上是关于深入理解Java虚拟机的主要内容,如果未能解决你的问题,请参考以下文章
深入理解Java虚拟机-如何利用VisualVM对高并发项目进行性能分析