关于后端:JVM简介

8次阅读

共计 5625 个字符,预计需要花费 15 分钟才能阅读完成。

[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 多平台公布

正文完
 0