Java虚拟机 (JVM--Java Virtual Machine)
html
对java虚拟机的介绍文章多如牛毛,写本文目的在于梳理一下,也方便之后翻来看看。
另外网上文章的图都挺丑的,本文90%的图都出于在下亲笔,如图有错误,请指出,定当当即更正
本文主要介绍一下Java虚拟机的抽象结构以及一些基础的概念
复制代码
有了JRE就能运行java程序,若是只是运行软件,装个JRE就好了。
咱们通常说java8,java10都是指的JDK,是java开发者使用的工具集,是一个大的概念,下面是java8的JDK组成
复制代码
JDK:Java 语言的软件开发工具包(
Java Development Kit
)
JRE: Java运行时环境(Java Runtime Environment
)
JVM: Java虚拟机(Java Virtual Machine
)java
虚拟机即:模拟计算机功能,并提供统一操做接口,从而实现代码在不一样平台的一致性。
从本质上来看,JVM是一个抽象接口,它有不少实现(以下),而这些实现也只是应用程序而已。
Java发展至今JVM也有过更新迭代,也有基于不一样场景下使用的JVM。算法
Sun Classic VM 第一款商用Java 虚拟机,纯解释器方式执行java代码。(已退出历史舞台)
EXact VM 编译器解释器混合工做,很快被HotSpot VM取代(已退出历史舞台)
HotSpot VM 沿用至今
KVM 手机端----效率低(已退出历史舞台)
JRockit 专一服务端应用
J9 IBM公司
Microsoft JVM windows----平台专用(已退出历史舞台)
Taobao VM 淘宝根据HotSpot VM定制
Dalvik 安卓虚拟机,寄存器架构,执行.dex文件(.class-->.dex)
复制代码
.class
文件和类加载器
.class
文件若是对class感兴趣,能够详见官方文档,对class文件介绍的很是细致,之后会写个专篇,这里不深刻。
java第一天便知道javac命令能将.java生成.class文件,而后就上手IDE,基本也就与class文件无缘了windows
关于
类加载器子系统
这里展现也不深刻,只要知道考它将java字节码文件载入JVM便可
详见:JVM篇2:[-加载器ClassLoader-]bash
程序计数器
当前线程私有,即每个线程都有一个,经过改变它来选取下一条须要执行的字节码指令
内存空间较小,JVM中惟一不会出现OutOfMemoryError状况
若是该线程正在执行一个本地方法,那么此时程序计数器的值是“undefined”
复制代码
Java虚拟机栈
调节参数:-Xss =
---->[1.栈帧(StatckFrame)]-------------------------------------------
|--- 一个线程的每一个方法在执行的同时,都会建立一个栈帧。
|--- 栈帧中存储的有局部变量表、操做站、动态连接、方法出口等。
|--- 当方法被调用时,栈帧入栈,当方法执行完成时,栈帧出栈。
---->[2.其余相关]-------------------------------------------
|--- 存取的速度快,仅次于寄存器
|--- 存储着方法的相关局部变量,包括各类基本数据类型,对象的引用,返回地址等。
|--- 局部变量表的内存空间在编译器完成分配,运行期不能改变其大小
---->[3.异常相关]-------------------------------------------
栈溢出: Java虚拟机会抛出StackOverflowError
内存溢出: 建立/动态扩展时没有足够的内存建立对应的Java虚拟机栈,抛出OutOfMemoryError异常
复制代码
Java虚拟机栈``本地方法栈
当前线程私有,本地方法栈支持Native方法调用
HotSpot VM将本地方法栈和Java虚拟机栈合二为一
异常状况:StackOverflowError和OutOfMemoryError
复制代码
关于线程私有和线程间共享,详见:Java内存模型(JMM--Java Memory Model)架构
方法区
-XX:PermSize =
-XX:MaxPermSize =
---->[1.基本介绍]-------------------------------------------
当前线程共享区域:用于存储已经被虚拟机加载的[类信息](即加载类时须要加载的信息,
包括版本、field、方法、接口等信息)、final常量、静态变量、编译器即时编译的代码等。
---->[2.运行时常量池]-------------------------------------------
|---用于存储编译期就生成的字面常量、符号引用、翻译出来的直接引用
|---存储在运行时产生的常量(好比String类的intern方法,做用是String维护了一个常量池,若是调用的字符“abc”已经在常量池中,则返回池中的字符串地址,不然,新建一个常量加入池中,并返回地址)
[符号引用]:编码是用字符串表示某个变量、接口的位置,直接引用就是根据符号引用翻译出来的地址,将在类连接阶段完成翻译
---->[3.异常相关]-------------------------------------------
异常状况:OutOfMemoryError异常。
复制代码
方法区、永久带和元空间简介
并发
方法区(Method Area):jvm规范里面的运行时数据区的一个组成部分(接口层面)
永久带(Perm):jdk7及以前的版本含有,是方法区的具体实现。(实现层面)
元空间(Metaspace):jdk8及以后的版本含有,是方法区的具体实现。(实现层面) # 元空间使用本地内存,不在JVM中
其实理解起来也不难:先定义和使用接口,再用具体实现完成工做。
复制代码
Java堆
Java 堆:存放对象
|---新生代:存储新生的对象
|--- Eden(E区):存放JVM中刚生成的Java对象
|--- Survivor(S区)
|--- FromSpace (S0)
|--- ToSpace (S1)
|---老年代:存储长期存活的对象
|--- 年龄达标(新生代中GC下存活的次数),可经过 -XX:MaxTenuringThreshold 指定
|--- 超大对象,直接进入老年代 。
非堆:存储程序运行时长期存活的对象,好比类的元数据、方法、常量、属性等。
复制代码
-XX:NewSize= 新生代大小
-XX:SurvivorRatio= E区与两个S区的比值 默认 8:2
-XX:NewRatio= 新生代和老年代的比值
-XX:MaxTenuringThreshold 进入老年代年龄阀值
-Xms 初始堆大小 : 默认是物理内存的1/64
-Xmx 最大堆大小 : 默认是物理内存的1/4
-XX:PermSize= 非堆内存初始值: 默认是物理内存的1/64
-XX:MaxPermSize= 最大非堆内存: 默认是物理内存的1/4
堆大小:新生代 + 老年代 + 持久代
复制代码
Minor GC : 清理新生代
Full GC : 清理整个堆空间
复制代码
通常新建立对象进入E区,当E区内存用完后,触发MinorGC。
存活的对象最终进入SurvivorFrom
复制代码
From
和To
1.当E区再度装满
2.触发MinorGC回收
3.存活的对象(包括From中)复制存放入To
4.To和From调换名称
复制代码
[1].为何要分新生代和老年代?
|--- 对象的生存情况不一样,使用不一样的回收算法,优化GC性能
|--- 在新生代对象可能被频繁建立或销毁(朝生夕死),老年代对象回收较少
[2].Survivor区存在的意义?
提升对象进入老年代的门槛,减小FullGC 的次数(FullGC很耗时)
[3].S0和S1有什么做用?
避免Survivor区空间的碎片化
复制代码
垃圾对象的断定
|---引用计数法
|---可达性分析
回收算法
|---标记清除
|---复制
|---标记整理
|---分代收集
垃圾回收器:
|---Serial
|---Parnew
|---CMS
|---G1
复制代码
在对象中添加引用计数器,当该对象被引用时,引用计数器+1,引用失效时,计数器-1
对象循环引用会失效
复制代码
Java中GC Root包括
:oracle
[1].虚拟机栈中的引用的对象。
[2].方法区中的类静态属性引用的对象。
[3].方法区中的常量引用的对象。
[4].本地方法栈(jni)即通常说的Native的引用对象。
复制代码
一个对象和GC Root之间没有连接,那么该对象可回收
好比ObjC和ObjB
之间的链接断开,橙色区域的对象可回收jvm
标记-清除(Mark-Sweep)
---->[方式]----------------------------------
[1].标记出全部须要存活的对象
[2].标记完成以后统一回收掉未被标记的对象。
---->[缺点]----------------------------------
[1].标记和清除效率都不高。
[2].会产生大量的不连续的内存碎片。
大量空间碎片的缺点:当程序须要为较大对象分配内存时,没法找到足够的连续内存而出发GC。
复制代码
标记-整理(Mark-Compact)
---->[方式]----------------------------------
[1].标记出全部存活的对象
[2].让全部存活的对象都向一端移动,在移动过程当中清理掉未标记的对象
---->[优劣]----------------------------------
优:不会产生大量不连续内存碎片问题
劣:要执行较多的复制操做,效率将会变低,不适合存活率高的状况
复制代码
复制算法(Copy)
---->[方式]----------------------------------
[1].将可用内存按容量分红大小相等的两块,每次只使用其中一块。
[2].回收时,将内存中存活的对象复制到另外一块内存,而后清理掉该内存空间
---->[优劣]----------------------------------
优:无空间碎片,实现简单,运行高效
劣:可以使用的内存降为原来一半
复制代码
这不是什么算法,而是根据不一样的代来使用什么时候的算法工具
新生代:可回收对象较多,回收率大。
|--- 复制算法,高效,无碎片,从E区到S区。
老年代:可回收对象少,回收率低。
|--- 标记-整理算法,无碎片。
复制代码
---->[新生代回收器:minor GC]----------------------------
[1].Serial(串行GC)-复制
[2].ParNew(并行GC)-复制
[3].Parallel Scavenge(并行回收GC)-复制
---->[老年代回收器:full GC]----------------------------
[4].Serial Old(MSC)(串行GC)-标记-整理
[5].CMS(并发GC)-标记-清除
[6].Parallel Old(并行GC)--标记-整理
---->[G1独立完成]-------------------------------
[7].G1(JDK1.7+)
复制代码
好了,综述就到这里,具体详情可见接下来的各篇专题。