JVM虚拟机机制理解

来源:互联网 发布:443端口号 编辑:程序博客网 时间:2024/06/03 17:06

JVM虚拟机机制

一.  什么是JVM

1.  JVM概念

1.1 JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java虚拟机包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法域。 JVM屏蔽了与具体操作系统平台相关的信息,使Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。JVM在执行字节码时,实际上最终还是把字节码解释成具体平台上的机器指令执行。

1.2 Java语言的一个非常重要的特点就是与平台的无关性。而使用Java虚拟机是实现这一特点的关键。一般的高级语言如果要在不同的平台上运行,至少需要编译成不同的目标代码。而引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。Java虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行。这就是Java的能够“一次编译,到处运行”的原因。

       2.  JDK/JRE/JVM的关系

JDK(Java Development Kit):是程序开发者用来编译、调试Java程序用的开发工具包。JDK工具也是Java程序,也需要JRE才能运行。为了保证JDK的独立性和完整性,在JDK安装过程中,JRE也是安装的一部分。所以在JDK的安装目录中下有一个名为jre的目录,用于存放JRE文件。

JRE(Java Runtime Environment):Java运行时环境,也是Java平台,所有的Java程序都要在JRE下才能运行。普通用户只需要运行已开发好的Java程序,安装JRE即可。

JVM(Java Virtual Machine):Java虚拟机,是JRE的一部分。他是一个虚拟出来的计算机,是通过实际的计算机仿真模拟各种计算机功能来实现的。JVM有自己完善的硬件架构,如处理器、堆栈、寄存器等,还有相应的指令系统。Java语言最重要的特点就是跨品台运行。使用JVM就是为了实现跨品台。

       

二.  JVM体系结构

1.  JVM内存模型


由此图可知JVM的内存模型主要为:

类装载子系统、垃圾回收器、执行引擎、运行时数据区 

类转载子系统(ClassLoader Sub-System):Java虚拟机中的类加载器分为两种:原始类加载器(Primordial Class Loader)和类加载器对象(ClassLoader Objects)。原始类加载器是Java虚拟机实现的一部分,类加载器对象时运行中的程序的一部分。

类加载器加载的过程:

1)加载:寻找并导入指定类型(类和接口)的二进制信息。

2)连接:进行验证、准备和解析

        a.验证:确保导入类型的正确性

        b.准备:为类型分配内存并初始化为默认值

        c.解析:将字符引用解析为直接引用

3)初始化:调用Java代码,初始化类变量为合适的值

垃圾回收器(Garbage Collection):负责回收那些在堆内存中没用的对象,即对象已经没有被引用了。

执行引擎(Execution Engine)::负责执行那些包含在被装载类的方法中的指令。

运行时数据区(Java Memory Allocation Area): 又叫虚拟机内存或Java内存,虚拟机运行时需要从整个计算机内存划分一块内存区域存储jvm需要用到的东西。而这个运行时数据区里面又会分为许多的小区。每个区都有自己不同的职责。后面会讲到每个区的作用和存储的内容。 

2.  运行时数据区

Java运行时数据区主要有程序计数器、虚拟机栈、本机方法栈、堆、方法区

 

2.1 程序计数器(Program Counter Register):

是一块较小的内存空间,它的作用是当前线程所执行的字节码的行号指示器,即当前线程所执行的代码为位置。在虚拟机中,字节码解释器的工作就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。

由于Java虚拟机的多线程是通过在多个线程之间做快速的切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个cpu只会执行一个线程中的指令。因此,为了线程切换后能恢复到之前的执行位置,每个线程都需要有一个独立的计数器,这些计数器间互不影响,而他们的内存空间也是线程私有的。

2.2 虚拟机栈:

这块区域是线程私有的,因为在jvm中,每个线程在执行每个方法是都会创建一个线帧,这个线帧用于存储方法中的局部变量、操作数栈、动态链接(在方法中调用其他方法)、方法返回等信息。每个方法从调用到结束都对应着一个线帧在虚拟机里面的入栈和出栈。

栈中存储了编译器已知八中基本数据类型

2.3 本地方法栈:

    这块区域在jvm运行内存中职责相对来说比较少,它只执行native方法,native方法底层都是有c语言编写的。如果这个区的内存不足也是会抛出StackOverflowError 和 OutOfMemoryError 异常。

2.4 堆:

这块区域是JVM中最大的一块,它存储的是jvm运行中的对象和数组。这个区域也是线程共享的,因为其中的对象和数组是需要被其他的对象所引用。

它也是垃圾回收的主要目标。因此,堆内存被分为新生代和老年代以及持久代(jdk1.8之后持久代被大大优化,改为了MetaSpace)。GC每隔一段时间就会对新生代进行垃圾回收。在分配对象遇到内存不足时,先对新生代进行GC;在新生代GC之后仍无法满足内存空间分配需求时,才会对整个堆空间以及方法区进行Full GC。而新生代有可以分为一个Eden区和两个Survivor区(FromSurvivor 和 To Survivor);它们的职责各不相同

Eden区:gc触发比较频繁的区域。存储的是新new的对象,几乎所有的对象都会经过Eden区。如果GC过后对象还未死亡就会将对象存储到Survivor区。

Survivor区:作为Eden区和OLD(老年代)的缓存。它是可以向老年代移动活动对象的实例。

2.5 方法区:

方法区和堆一样是线程共享的,它主要存储的是被虚拟机加载的类信息、常量、静态变量、及时编译期编译后的代码等数据。GC也会对这块区域进行垃圾回收,但相对堆内存的回收来说,这块区域的回收就相对少了很多。

如果对方法区进行细分的话,它还可以分为运行时常量区。主要存储class文件中的版本、字段、方法、接口等描述信息。运行时常量区还可以分为信息常量池。它主要存储编译器生成的各种字面量和符号引用。

 

三.  JVM编译和执行过程

1. Java源码的编译

Java源码的编译Java源码编译器来完成的,其过程是


2. Java字节码的执行

Java字节码文件的执行是由JVM执行引擎来完成的。其过程如下:


四.  JVM垃圾回收

1.  什么是垃圾回收及其出现的意义

1.1 GC(GarbageCollection)垃圾回收:Java语言的核心技术之一,垃圾收集 的目的在于清除不再使用的对象。GC通过确定对象是否被活动对象引用来确定是否收集该对象。

1.2  GC的意义:java语言的一个显著特点就是引入了Java回收机制,是c++程序员最头疼的内存管理的问题迎刃而解,它使得java程序员在编写程序的时候不在考虑内存管理。由于有个垃圾回收机制,java中的额对象不在有“作用域”的概念,只有对象的引用才有“作用域”。垃圾回收可以有效的防止内存泄露,有效的使用空闲的内存;

2.  垃圾回收的算法

2.1 Java语言规范没有明确说明JVM使用哪种垃圾回收算法,但是任何一种垃圾回收算法一般要做两件基本事情:(1)发现无用的信息对象;(2)回收无用对象所占用的内存空间,是该空间可被程序在此使用。

2.2 常见算法:引用计数法、tracing算法或标记-清除算法、compacting算法或标记-整理算法、copying算法、generation算法

2.3 generation算法

      分代的垃圾回收策略,是基于不同的对象的生命周期是不一样的,因此不同生命周期对象可以采用不同的回收算法,以便提高回收效率。

新生代(Young Generation):

1. 所有新生成的对象首先都是存放在新生代的。新生代的目标就是尽可能快速的收集掉那些生命周期短的对象。

2.  新生代内存按照8:1:1的比例分为一个eden区和两个survivor(survivor0,survivor1)区。一个Eden区,两个 Survivor区(一般而言)。大部分对象在Eden区中生成。回收时先将eden区存活对象复制到一个survivor0区,然后清空eden区,当这个survivor0区也存放满了时,则将eden区和survivor0区存活对象复制到另一个survivor1区,然后清空eden和这个survivor0区,此时survivor0区是空的,然后将survivor0区和survivor1区交换,即保持survivor1区为空, 如此往复。

3.  当survivor1区不足以存放eden和survivor0的存活对象时,就将存活对象直接存放到老年代。若是老年代也满了就会触发一次Full GC,也就是新生代、老年代都进行回收。

4.  新生代发生的GC也叫做Minor GC,MinorGC发生频率比较高(不一定等Eden区满了才触发)。

老年代(Old Generation):

1.  在年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代中。因此,可以认为老年代中存放的都是一些生命周期较长的对象

2.  内存比新生代也大很多(大概比例是1:2),当老年代内存满时触发Major GC即Full GC,FullGC发生频率比较低,老年代对象存活时间比较长,存活率标记高。

    持久代(Permanent Generation):

1.  在jdk1.8之后对其进行了优化,改名为MetaSpace。用于存放静态文件,

如Java类、方法等。

3.  垃圾回收器

新生代垃圾回收器:Serial、PraNew、Parallel Scavenge

老年代垃圾回收器:Serial Old、Parallel Old、CMS

4.  GC执行的过程

由于对象进行了分代处理,因此垃圾回收区域、时间也不一样。GC有两种类型:Scavenge GC和FullGC

ScavengeGC

 

一般情况下,当新对象生成,并且在Eden申请空间失败时,就会触发Scavenge GC,对Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区。这种方式的GC是对年轻代的Eden区进行,不会影响到年老代。因为大部分对象都是从Eden区开始的,同时Eden区不会分配的很大,所以Eden区的GC会频繁进行。因而,一般在这里需要使用速度快、效率高的算法,使Eden去能尽快空闲出来。

 

Full GC

 

对整个堆进行整理,包括Young、Tenured和Perm。Full GC因为需要对整个堆进行回收,所以比Scavenge GC要慢,因此应该尽可能减少Full GC的次数。在对JVM调优的过程中,很大一部分工作就是对于FullGC的调节。有如下原因可能导致Full GC:

1.年老代(Tenured)被写满

2.持久代(Perm)被写满

3.System.gc()被显示调用

4.上一次GC之后Heap的各域分配策略动态变化

 

1 0
原创粉丝点击