[TOC]

一、什么是JVM

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

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

二、JVM的组成

咱们先把JVM这个虚拟机画进去,如下图所示:

从这张图中咱们能够看出,JVM是运行在操作系统之上的,它与硬件没有间接的交互,咱们再来看JVM由哪些局部组成,如下图所示:

1、Class Loader 类加载器

  类加载器的作用是加载类文件到内存,比方编写一个HelloWord.java 程序,而后通过javac 编译成class 文件,那怎么能力加载到内存中被执行呢?Class Loader 承当的就是这个责任,那不可能轻易建设一个.class 文件就能被加载的,Class Loader 加载的class 文件是有格局要求。

Class Loader 只管加载,只有合乎文件构造就加载,至于说能不能运行,则不是它负责的,那是由Execution Engine 负责的。

2、Execution Engine 执行引擎

  执行引擎也叫做解释器(Interpreter) ,负责解释命令,提交操作系统执行。

3、Native Interface 本地接口

  本地接口的作用是交融不同的编程语言为Java 所用,它的初衷是交融C/C++ 程序,Java 诞生的时候是C/C++ 横行的时候,要想立足,必须有一个聪慧的、睿智的调用C/C++ 程序,于是就在内存中专门开拓了一块区域解决标记为native 的代码,它的具体做法是Native Method Stack 中注销native 办法,在Execution Engine 执行时加载native libraies 。目前该办法应用的是越来越少了,除非是与硬件无关的利用,比方通过Java 程序驱动打印机,或者Java 系统管理生产设施,在企业级利用中曾经比拟少见,因为当初的异构畛域间的通信很发达,比方能够应用Socket 通信,也能够应用Web Service 等等,不多做介绍。

4、 Runtime data area 运行数据区

  运行数据区是整个JVM 的重点。咱们所有写的程序都被加载到这里,之后才开始运行,Java 生态系统如此的凋敝,得益于该区域的低劣自治。

整个JVM 框架由加载器加载文件,而后执行器在内存中解决数据,须要与异构零碎交互是能够通过本地接口进行,瞧,一个残缺的零碎诞生了!

三、JVM的内存治理

所有的数据和程序都是在运行数据区寄存,它包含以下几局部:

1、 Stack 栈

栈也叫栈内存,是Java程序的运行区,是在线程创立时创立,它的生命期是追随线程的生命期,线程完结栈内存也就开释,对于栈来说不存在垃圾回收问题,只有线程一完结,该栈就Over。问题进去了:栈中存的是那些数据呢?又什么是格局呢?

栈中的数据都是以栈帧(Stack Frame)的格局存在,栈帧是一个内存区块,是一个数据集,是一个无关办法(Method)和运行期数据的数据集,当一个办法A被调用时就产生了一个栈帧F1,并被压入到栈中,A办法又调用了B办法,于是产生栈帧F2也被压入栈,执行结束后,先弹出F2栈帧,再弹出F1栈帧,遵循“先进后出”准则。

那栈帧中到底存在着什么数据呢?栈帧中次要保留3类数据:本地变量(Local Variables),包含输出参数和输入参数以及办法内的变量;栈操作(Operand Stack),记录出栈、入栈的操作;栈帧数据(Frame Data),包含类文件、办法等等。光说比拟干燥,咱们画个图来了解一下Java栈,如下图所示:

图示在一个栈中有两个栈帧,栈帧2是最先被调用的办法,先入栈,而后办法2又调用了办法1,栈帧1处于栈顶的地位,栈帧2处于栈底,执行结束后,顺次弹出栈帧1和栈帧2,线程完结,栈开释。

2、Heap 堆内存

jvm中分为堆和办法区,又进一步分为新生代和老年代,办法区为永恒代。

 堆中辨别的新生代和老年代是为了垃圾回收,新生代中的对象存活期个别不长,而老年代中的对象存活期较长,所以当垃圾回收器回收内存时,新生代中垃圾回收成果较好,会回收大量的内存,而老年代中回收成果较差,内存回收不会太多。

基于以上个性,新生代中个别采纳复制算法,因为存活下来的对象是多数,所须要复制的对象少,而老年代对象存活多,不适宜采纳复制算法,个别是标记整顿和标记革除算法。

因为复制算法须要留出一块独自的内存空间来以备垃圾回收时复制对象应用,所以将新生代分为eden区和两个survivor区,每次应用eden和一个survivor区,另一个survivor作为备用的对象复制内存区。

一个JVM实例只存在一个堆类存,堆内存的大小是能够调节的。类加载器读取了类文件后,须要把类、办法、常变量放到堆内存中,以不便执行器执行,堆内存分为三局部:

  Permanent Space 永恒存储区

永恒存储区是一个常驻内存区域,用于寄存JDK本身所携带的Class,Interface的元数据,也就是说它存储的是运行环境必须的类信息,被装载进此区域的数据是不会被垃圾回收器回收掉的,敞开JVM才会开释此区域所占用的内存。

  Young Generation Space 新生区

新生区是类的诞生、成长、沦亡的区域,一个类在这里产生,利用,最初被垃圾回收器收集,完结生命。新生区又分为两局部:伊甸区(Eden space)和幸存者区(Survivor pace),所有的类都是在伊甸区被new进去的。幸存区有两个: 0区(Survivor 0 space)和1区(Survivor 1 space)。当伊甸园的空间用完时,程序又须要创建对象,JVM的垃圾回收器将对伊甸园区进行垃圾回收,将伊甸园区中的不再被其余对象所援用的对象进行销毁。而后将伊甸园中的残余对象挪动到幸存0区。若幸存0区也满了,再对该区进行垃圾回收,而后挪动到1区。那如果1区也满了呢?再挪动到养老区。

  Tenure generation space养老区

养老区用于保留从新生区筛选进去的JAVA对象,个别池对象都在这个区域沉闷。 三个区的示意图如下:

3、 Method Area 办法区

办法区是被所有线程共享,该区域保留所有字段和办法字节码,以及一些非凡办法如构造函数,接口代码也在此定义。

4、PC Register 程序计数器

每个线程都有一个程序计数器,就是一个指针,指向办法区中的办法字节码,由执行引擎读取下一条指令。

5、Native Method Stack 本地办法栈

四、JVM垃圾回收

  GC (Garbage Collection)的基本原理:将内存中不再被应用的对象进行回收,GC中用于回收的办法称为收集器,因为GC须要耗费一些资源和工夫,Java在对对象的生命周期特色进行剖析后,依照新生代、旧生代的形式来对对象进行收集,以尽可能的缩短GC对利用造成的暂停

(1)对新生代的对象的收集称为minor GC;

(2)对旧生代的对象的收集称为Full GC;

(3)程序中被动调用System.gc()强制执行的GC为Full GC。

不同的对象援用类型, GC会采纳不同的办法进行回收,JVM对象的援用分为了四种类型:

(1)强援用:默认状况下,对象采纳的均为强援用(这个对象的实例没有其余对象援用,GC时才会被回收)

(2)软援用:软援用是Java中提供的一种比拟适宜于缓存场景的利用(只有在内存不够用的状况下才会被GC)

(3)弱援用:在GC时肯定会被GC回收

(4)虚援用:因为虚援用只是用来得悉对象是否被GC

五、JVM相干问题

问:堆和栈有什么区别

答:堆是寄存对象的,然而对象内的长期变量是存在栈内存中,如例子中的methodVar是在运行期寄存到栈中的。

栈是追随线程的,有线程就有栈,堆是追随JVM的,有JVM就有堆内存。

问:堆内存中到底存在着什么货色?

答:对象,包含对象变量以及对象办法。

问:类变量和实例变量有什么区别?

答:动态变量是类变量,非动态变量是实例变量,直白的说,有static润饰的变量是动态变量,没有static润饰的变量是实例变量。动态变量存在办法区中,实例变量存在堆内存中。

问:我据说类变量是在JVM启动时就初始化好的,和你这说的不同呀!

答:那你是一人传虚;万人传实,信我的,没错。

问:Java的办法(函数)到底是传值还是传址?

答:都不是,是以传值的形式传递地址,具体的说原生数据类型传递的值,援用类型传递的地址。对于原始数据类型,JVM的解决办法是从Method Area或Heap中拷贝到Stack,而后运行frame中的办法,运行结束后再把变量指拷贝回去。

问:为什么会产生OutOfMemory产生?

答:一句话:Heap内存中没有足够的可用内存了。这句话要好好了解,不是说Heap没有内存了,是说新申请内存的对象大于Heap闲暇内存,比方当初Heap还闲暇1M,然而新申请的内存须要1.1M,于是就会报OutOfMemory了,可能当前的对象申请的内存都只有0.9M,于是就只呈现一次OutOfMemory,GC也失常了,看起来像偶发事件,就是这么回事。 但如果此时GC没有回收就会产生挂起状况,零碎不响应了。

问:我产生的对象不多呀,为什么还会产生OutOfMemory?

答:你继承档次忒多了,Heap中 产生的对象是先产生 父类,而后才产生子类,明确不?

问:OutOfMemory谬误分几种?

答:分两种,别离是“OutOfMemoryError:java heap size”和”OutOfMemoryError: PermGen space”,两种都是内存溢出,heap size是说申请不到新的内存了,这个很常见,查看利用或调整堆内存大小。

“PermGen space”是因为永恒存储区满了,这个也很常见,个别在热公布的环境中呈现,是因为每次公布利用零碎都不重启,长此以往永恒存储区中的死对象太多导致新对象无奈申请内存,个别重新启动一下即可。

问:为什么会产生StackOverflowError?

答:因为一个线程把Stack内存全副耗尽了,个别是递归函数造成的。

问:一个机器上能够看多个JVM吗?JVM之间能够互访吗?

答:能够多个JVM,只有机器接受得了。JVM之间是不能够互访,你不能在A-JVM中拜访B-JVM的Heap内存,这是不可能的。在以前老版本的JVM中,会呈现A-JVM Crack后影响到B-JVM,当初版本十分少见。

问:为什么Java要采纳垃圾回收机制,而不采纳C/C++的显式内存治理?

答:为了简略,内存治理不是每个程序员都能折腾好的。

问:为什么你没有具体介绍垃圾回收机制?

答:垃圾回收机制每个JVM都不同,JVM Specification只是定义了要主动开释内存,也就是说它只定义了垃圾回收的形象办法,具体怎么实现各个厂商都不同,算法各异,这货色切实没必要深刻。

问:JVM中到底哪些区域是共享的?哪些是公有的?

答:Heap和Method Area是共享的,其余都是公有的,

问:什么是JIT,你怎么没说?

答:JIT是指Just In Time,有的文档把JIT作为JVM的一个部件来介绍,有的是作为执行引擎的一部分来介绍,这都能了解。Java刚诞生的时候是一个解释性语言,别嘘,即便编译成了字节码(byte code)也是针对JVM的,它须要再次翻译成原生代码(native code)能力被机器执行,于是效率的担心就提出来了。Sun为了解决该问题提出了一套新的机制,好,你想编译成原生代码,没问题,我在JVM上提供一个工具,把字节码编译成原生码,下次你来拜访的时候间接拜访原生码就成了,于是JIT就诞生了,就这么回事。

问:JVM还有哪些局部是你没有提到的?

答:JVM是一个异样简单的货色,写一本砖头书都不为过,还有几个要阐明的:

常量池(constant pool):依照程序存放程序中的常量,并且进行索引编号的区域。比方int i =100,这个100就放在常量池中。

平安管理器(Security Manager):提供Java运行期的安全控制,避免歹意攻打,比方指定读取文件,写入文件权限,网络拜访,创立过程等等,Class Loader在Security Manager认证通过后能力加载class文件的。

办法索引表(Methods table),记录的是每个method的地址信息,Stack和Heap中的地址指针其实是指向Methods table地址。

问:为什么不倡议在程序中显式的生命System.gc()?

答:因为显式申明是做堆内存全扫描,也就是Full GC,是须要进行所有的流动的(Stop The World Collection),你的利用能接受这个吗?

问:JVM有哪些调整参数?

答:十分多,本人去找,堆内存、栈内存的大小都能够定义,甚至是堆内存的三个局部、新生代的各个比例都能调整。

本文由mdnice多平台公布