带你领略JVM全貌
Posted 风某人~Wind
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了带你领略JVM全貌相关的知识,希望对你有一定的参考价值。
JVM,全称Java Virtual Machine (Java虚拟机)就是一个虚构出来的虚拟机,是通过在实际的计算机上模拟计算机的各种功能来实现的。现在jdk1.8的虚拟机是由HoSpot来实现的。
我们常说,Java是一种跨平台的语言,其实就是因为Java虚拟机跨平台,Java虚拟机其实和java并没有多大关系,他只和class文件有关系,任何一门语言,只要你能编译成class文件,就能在Jvm上执行,文章有点长,请耐心看完。
Java类加载过程
1. 一个.java文件会先通过javac前端编译器把java文件编译成class字节码文件
2. 然后通过类加载器把class文件加载到内存中,通过双亲委派机制进行依次加载,
3. 加载到内存以后需要对class文件进行验证,判断class文件是否合法。
4. 为class类文件中的静态变量赋初始值,比如 int = 0; int 初始值就是0;
5. 把常量池中的符号引用变为直接引用,就是把常量池中的类,属性等变为指向具体内存的地址。
6. 把静态变量赋默认值。
双亲委派机制
一个class文件,它需要被加载到内存中,是通过类加载器去加载的,每个类加载器自身都维护了一个缓存。
1. 首先加载一个class文件,它会先去自定义类加载器缓存中找,如果没找到,再往上找,一直找到Bootstrap类加载器了,还没找到的话,bootstarp 就会判断这个class文件是否属于它加载的范围,因为每个类加载器都有加载的范围,如果没找到,就会往下通知下一级的类加载器把这个class文件加载进来,直到把这个class文件加载进来,如果到自定义类加载器中还没找到这个class文件,就会报异常: class not found ;
类加载器加载范围:
Bootstarp类加载器:主要加载lib/rt.jar包,charset.jar包等核心类
Extension类加载器:主要加载扩展包,jre/lib/ext/*.jar 下面的包
App类加载器:主要加载classpath中的一些class文件
自定义类加载器: 自定义类加载范围
双亲委派模式作用:主要是为了安全。
JVM内存模型
1. 方法区(Method Area)
主要存储已经被JVM加载的常量,静态变量,该区属于线程共享区。
2. 堆(Heap)
堆在jvm虚拟机启动时创建,一般所有初始化的实例都是在堆中开辟空间来存放。
堆中内存一般分为新生代和老年代
1. 新生代区域中,用来存放新生对象,划分了一个 eden 区和两个Survivor区,内存比例为8:1:1 。
2. 老年代区域中,存放的一些顽固分子(分代年龄超过15的)和一些比较大的对象。
分代年龄:每个对象的对象头上,一般都会记录锁标记和分代年龄,分代年龄只分配了4bit 去记录,所以最大值为15,即(1111 二进制转化为十进制就是15 ,16就是5bit了),每次Eden区触发垃圾回收的时候,当这个对象经过这次垃圾回收之后,还存在,则就是没有被回收的情况下,分代年龄+1;
3. 虚拟机栈(VM stack)
虚拟机栈是属于线程私有的,它里面存的是一个个的栈帧,每个线程都会创建一个栈帧,线程所有的局部变量都会存在栈帧的局部变量表中,它也会根据线程结束而销毁。
每个栈帧都有以下几块区域:
1. 局部变量表:用来专门存储局部变量信息的。
2. 操作数栈:用来计算值的栈空间.
3. 动态链接:当前这个方法如果需要调用其他方法,这个链接会指定其他方法的地址链接.
4. 方法返回地址:当前这个方法执行完以后,需要返回的地址。
4. 本地方法栈(Navive Method stack)
里面存储都是供虚拟机调用的一些系统方法服务,不属于java的方法。
5. 程序计数器(Program Counter Register)
主要用来记录字节码文件运行到哪个位置的指针,就比如说,字节码文件跑到30行,它就是指在这个地方的指针。
JVM对垃圾的定义
当这个对象没有任何一个引用指向他的时候,就可以称这个对象为垃圾。
判断一个对象是否为垃圾的方法
1. 引用计数器
每个对象上都有一个引用计数器,用来统计一共有多少个引用指向它,如果这个引用为0的时候,系统就判定这个对象为垃圾,但是这种解决不了循环引用的垃圾。
循环引用:A对象引用B对象,B对象引用A对象,就会导致这两个对象都无法回收。
2. 根可达算法
根据JVM的根对象去找,如果没有被根对象直接或者间接引用的对象都是垃圾。
JVM根对象包括:JVM栈变量,静态变量,常量池。
JVM垃圾回收算法
1. 标记清除算法
先对垃圾进行标记,然后一个一个的清除。
缺点:内存中会存在很多碎片化的片段,上图中b,d区域中的空间回收了以后,就产生了 这种断断续续的碎片化空间
2. 标记整理算法
标记整理算法会在标记清除的基础上,在对内存空间进行整理
优点:没有碎片化的空间
缺点:效率上没有标记清除快,需要对内存操作两次,1次清除,1次整理。
3. 拷贝算法
拷贝算法会先把A空间中的存活对象全部先拷贝到B空间,然后把A空间所有数据全部清空。
优点:速度效率比较快
缺点:比较浪费空间
JVM垃圾回收器
线程安全点: 当系统执行FGC的时候,在所有业务线程会进入线程安全点,这时候所有业务线程都会停止,垃圾回收线程启动,执行垃圾回收。
1. serial new + serial old
单线程串行垃圾回收器,serial new 新生代使用的拷贝算法,serial old 老年代使用的是标记整理算法,以前老版本才会使用的,现在已经过时了。
2. parallel scavenge + parallel old
jdk1.8 默认垃圾回收器,多线程并行垃圾回收期, parallel scavenge 新生代使用的是拷贝算法,parallel old 老年代使用的是标记整理算法
3. parnew + cms
cms 是一个并行的垃圾回收器,这个并行和ps + po 不同,它这个垃圾回收器进行垃圾回收的时候,业务线程不会停止,还可以继续响应,只不过吞吐量比较慢。
4. G1
G1 也是一个并行的垃圾回收器,他也是在执行垃圾回收的时候,业务线程不会停止,这点和CMS很像,但是他是采用内存分区的方式来管理堆内存的,回收的时候是以区为单位进行回收的,jdk1.9默认指定的垃圾回收器就是G1。
JVM什么时候触发垃圾回收?
分两种
1. CMS和G1垃圾回收器,新生代在内存满的时候进行回收,老年代一般在百分之多少可以回收,这个值可以设置,默认为92%;
2.其他垃圾回收器,都是在新生代满的时候触发YGC,老年代满了,新生代也满了就会触发FGC,然后老年代和新生代一起进行垃圾回收。
如有不正确的地方,欢迎补充,创作不易,欢迎点赞收藏!
以上是关于带你领略JVM全貌的主要内容,如果未能解决你的问题,请参考以下文章