JVM——Java虚拟机的浅析

来源:互联网 发布:淘宝压缩图片 编辑:程序博客网 时间:2024/04/29 08:51

JVM,全称为Java Virtual Machine(Java虚拟机)。本文之所以为java虚拟机的浅析,是因为个人觉得java虚拟机是一个很大的总体,本博文不能全部描述出来。

一、Java的跨平台性

Java作为当前最流行的开发语言之一,可以用在javaweb开发和android的开发,也可以运行在其他平台上,Java的口号是“一次编译,到处运行”。之所以会有这样的跨平台性,因为存在JVM也叫java虚拟机。

二、Java文件的执行过程。

JVM是个标准,具体上VM有几十个实现,主流的包括Hotspot, Jikes RVM等基于C/C++和汇编。

                Java学习交流QQ群:495273252 我们一起学Java!


如上图所示,java程序编写玩以后,通过加载——链接——初始化,在jvm里面运行。步骤如下:

1、java程序代码在我们写好以后,生成war包放在tomcat里面运行,这时war包会被解压,通过编译之后,之后生成字节码文件(class文件);

2、通过类加载器加载class文件到JVM里面里面的内存(包括方法区、堆内存和栈内存)。(加载

        3、根据jdk的系统的版本找到jvm.cfg的配置文件,路径为:C:\Program Files\Java\jdk1.8.0_101\jre\lib\amd64\jvm.cfg。(链接)

      4、根据jvm.cfg的配置,找到jvm.dll的文件,这个jvm.dll文件可以看作为java虚拟机的主要实现。(链接)

      5、初始化jvm,获得JNI接口,其中JNI(java native interface)也叫做java本地接口,通过JNI接口装载class文件。(初始化)

      6、找到main方法并运行。

三、jvm的内存结构

      java文件生成class文件之后,通过jvm的加载操作,可以将class文件加载到内存里面。

内存空间包括方法区、堆内存、java栈、还包括PC Registers程序计数器和本地方法栈,程序计数器是当前线程所执行字节码的行号指示器,所以它是私有的. 如果线程执行的是非native方法,则程序计数器中保存的是当前需要执行的指令的地址:如果线程执行的是native方法,则程序计数器中的值是undefined. 。本地方法栈,表示的是java程序,在jvm虚拟机里面,可以调用本地方法,当虚拟机调用本地方法时,会保持java栈不变,不再线程的java栈中压入新的栈帧,只是简单的动态链接并直接调用本地的方法。


preview               


(如上图,内存空间是五彩中心区,也叫做运行时数据区)

       本博文重点讲解内存里面的方法区、java堆和java栈。

java虚拟机内存最主要的也是这三块:

       (一)、方法区(Method Area):“.class文件”被类加载后的信息,常量,静态变量存放在这儿。是全局变量。方法区也叫做

Non-Heap(非堆),是为了和java堆区别开来。方法区包括运行时常量池(Run-time Constant Pool)它是每个私有的。另外一个是“字符串常量池(String Pool)”。和运行时常量池不是一个概念。字符串常量池是全局共享的。位置就在第二张图里Interned String的位置,可以理解为在永生代里,方法区外面。HotSpot虚拟机处理方法区的时候选择把GC分代收集扩展至方法区,或者说使用永久代来实现方法区而已,这样HotSpot的垃圾收集器可以像管理Java堆一样管理这部分内存,能够省去专门为方法区编写内存管理代码的工作。可是其他的虚拟机并不会这样处理。

preview

preview



编译成class文件以后,还有一个也叫“常量池(Constant Pool Table)”的东西(淡绿色区块)。但这个常量池和方法区里面的运行时常量池和字符串常量池不是一个东西。class文件的常量池主要存在两个东西,“字面量(Literal)”“符号引用量(Symbolic References)”“字面量(Literal)”“符号引用量(Symbolic References)”其中字面量就包括类中定义的一些常量,因为String是不可变的,是由final关键字修饰的。一般的“Hello”字符串,就是作为字面量(常量)写在class的常量池里。

运行.class文件的时候,文件的信息,就会被解析到内存的方法区里。class文件里面的常连池里面的信息,会被保存在运行时常量池里面。但是String不是,比如String str1 = "Hello",这个“Hello”的一个引用会被存到字符串常量池里面。

 (二)、java栈内存(java stacks)

java栈全称为“虚拟机栈(JVM Stacks)”。存放基本型,以及对象引用str1。线程私有。java的栈里面有两个关键的知识点,也就是“局部变量表(Local Variables)”“操作数栈(Operand Stack)”。在虚拟机里面,因为栈是私有的,每次执行一个方法,就会形成一个栈帧(Stack Frame),并且保存在栈里面,进行入栈。每一个栈帧都维护着一个局部变量表和操作数栈。局部变量表里面存放的就是基本型和对象引用。操作数栈就是执行者,执行具体的操作:

preview


preview


列如执行加法100+98,这样可以看出,操作数栈的操作次数很多,先要把100存进去,98也存进去,再进行相加为198也存进去,最后存在到局部变量表里面。如果java栈空间不足了,程序会抛出StackOverflowError异常,想一想什么情况下会容易产生这个错误,对,递归,递归如果深度很深,就会执行大量的方法,方法越多java栈的占用空间越大。


preview

 (三)、堆内存(Heap Memory)

是java内存空间里面的最大的一个内存。存放对象的实例和数组。全局共享。一个程序在运行的时候,可以想象,需要很多次的实例化对象,开辟空间,这样需要频繁的操作堆内存,如果堆内存不足,OutOfMemoryError异常。

下面我们来分析一下:

String str1 = "Hello";

String str2 = "Hello";

String str3 = new String("Hello");

在执行第一个语句时,这个"Hello"的一个引用会被保存在方法区里面的的一个引用会被存到同样在Non Heap区的字符串常量池(String Pool)里。而“Hello”本体还是和所有对象一样,创建在Heap堆区。具体位置是在新生代的Eden区。(这样创建了两个实例)。

在执行第二个语句时,虚拟机会先到字符串常量池里面找,看有没有能equals("Hello")的String,如果找到了,就在栈帧的局部变量表里面创建str2变量,然后把字符串常量池里对Hello的引用复制给str变量。找不到的话,才会在heap堆里面重新创建一个对象。(这里创建了一个实例)

在执行第三个语句时,是在heap堆里面,str3因为new关键字会在Heap堆申请一块全新的内存,来创建新对象。虽然字面还是"Hello",但是完全不同的对象,有不同的内存地址。

preview

preview

preview

参考:http://www.jianshu.com/p/ec971f4c6c04?ref=myread

https://www.zhihu.com/question/22739143

http://blog.csdn.net/sunshine__me/article/details/49992909

https://www.zhihu.com/question/20163831

http://blog.csdn.net/wangyy130/article/details/52105217

http://www.cnblogs.com/wade-luffy/p/5813747.html

原创粉丝点击