带你领略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全貌的主要内容,如果未能解决你的问题,请参考以下文章

带你领略JVM全貌

阿里大神带你领略Java一致性算法的风骚!(PaxosZabRaft选举NWRHashGossip)

带你领略算法艺术

带你领略算法艺术

带你领略算法艺术

带你领略算法艺术