查看: 160|回复: 0

Java虚拟机——JVM

[复制链接]
发表于 2020-2-20 16:54:28 | 显示全部楼层 |阅读模式
一、JVM整体架构
1、JVM(Java虚拟机):指以软件的方式模仿具有完整硬件系统功能运行在一个完全隔离环境中的完整盘算机系统,是物理机的软件实现。常用的虚拟机有VMWare、Virtual Box、Java Virtual Machine。

2、JVM由三个重要的子系统构成


  • 类加载子系统 (即类加载器)
  • 运行时数据区(即内存结构 / 内存模型 / JMM)
  • 实行引擎(包罗垃圾收集器)

通过类加载器将.class文件加载进内存中,再由实行引擎去运行。
堆和方法区是所有线程都共享的。
Java栈、本地方法栈和程序计数器是非线程共享的。
二、JVM内存结构
下面会用到的例子:定义一个Math类。

1、本地方法栈(线程私有):登记native本地方法(即native方法是存到本地方法栈),在实行引擎实行时加载本地方法库(C语言实现的库)。
举例:Thread类中的 start0() 方法底层实现是用C语言写的。

2、程序计数器(线程私有):就是一个指针,指向方法区中的方法字节码(用来存储指向下一条指令的地址,也即指向将要实行的指令代码),由实行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不计。
举例:对.class文件举行 javap -c 反编译,下图为 math() 方法反编译后的效果:(iconst_1和istore_1两条指令代表 int a=1)

3、方法区(线程共享):类的所有字段和方法字节码,以及一些特殊方法如构造函数、接口代码也定义在此(类加载器会将字节码文件加载到方法区中,分解成多个部分)。简朴说,所有定义的方法的信息都保存在该地区,静态变量+常量+类信息(构造方法/接口定义)+运行时常量池都存在方法区中,虽然JVM规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做Non-Heap(非堆),目的应该是与Java堆区分开来。
4、Java栈(线程私有):Java线程实行方法的内存模型,一个线程对应一个栈,每个方法在实行的同时都会创建一个栈帧(用于存储局部变量表、操作数栈、动态链接、方法出口等信息),不存在垃圾回收问题,只要线程一结束该栈就释放,生命周期和线程一致。
JVM对该地区规范了两种异常:
① 线程请求的栈深度大于虚拟机栈所允许的深度时,将抛出StackOverFlowError异常。
② 若虚拟机栈可动态扩展,当无法申请到足够内存空间时将抛出OutOfMemoryError,通过JVM参数 -> Xss指定栈空间,空间大小决定函数调用的深度。
下图左边为Java栈的内存结构图:

动态链接:如Map map = new HashMap(),程序运行时map变量必要去找到运行时的HashMap实例对象(多态)的过程,就叫动态链接。
方法出口:如main()方法中的math()方法运行结束return返回的值用于main()方法中,return的这个过程就叫方法出口。
5、堆(线程共享): 虚拟机启动时创建,用于存放对象实例,几乎所有的对象(包罗常量池)都放在堆上分配内存,当对象无法在该空间申请到内存时将抛出OutOfMemoryError异常。同时也是垃圾收集器管理的重要地区。可通过 -Xmx -Xms 参数来分别指定最大堆和最小堆。
下图为堆的内存结构图:

JVM调优的根本目的:只管减少Full GC的次数,并且缩短每次Full GC的时间长度。(Full GC性能非常低,实行这个过程时会停止整个JVM,即STW(Stop The World),包罗程序运行的那些线程,然后专门去做垃圾收集。现在新的GC会只管缩短STW时间长度,只管让垃圾收集和业务线程并发实行)
垃圾收集器(GC)存在的意义:回收堆中无引用的对象,释放空间。
堆中新生代(Young Generation)占1/3的堆空间,新生代包罗伊甸园区(Eden Space)和幸存者区(Survivor Space);老年代(Old Generation)占2/3的堆空间。
JDK1.8从前是没有元数据区(MetaData Space)的,而是永世代,从1.8开始元空间取代了永世代,本质和永世代雷同,都是对JVM规范中方法区的实现,区别在于元数据区并不在虚拟机中,不属于堆中的内存结构,而是直接使用本机物理内存,永世代在虚拟机中,逻辑结构上属于堆,但是物理上不属于堆,堆大小=新生代+老年代。元数据区也有可能发生OutOfMemory异常。


  • JDK1.6及之前:有永世代,常量池在方法区
  • JDK1.7:有永世代,但已逐步“去永世代”,常量池在堆
  • JDK1.8及之后:无永世代,常量池在元空间
提问:为什么JDK1.8用元数据区取代了永世代?
官方解释:移除永世代是为融合HotSpot JVM与JRockit VM而做出的努力,由于JRockit没有永世代,不必要设置永世代。
(JDK 8实际上是这两种虚拟机合并之后的产物,HotSpot JVM(Sun公司开辟),JRockit VM(BEA公司开辟)。猜测:HotSpot JVM输了,没错,开辟出Java语言的Sun公司最后输了,就听JRockit VM的)
① new出来的对象会放在Eden区中,当Eden区占满时会做一次Minor GC(即轻GC),回收Eden区中无引用的对象,剩下的存活对象则会放到From区中。
② 后面又有新的new出来的对象不断存放在Eden区中,当Eden区占满时再做Minor GC,又有对象存放在From区中,当From区占满时也会做Minor GC,GC会回收Eden区和From区中无引用的对象,剩下的存活对象则会放到To区中,此时角色变化,From区变成To区,To区变成From区。
③ 接着,还会有新的对象存放到Eden区中,当Eden区占满时再做Minor GC,此时存活的对象放到新的From区中,当新的From区放满时又做Minor GC,把存活对象放入新的To区,依次轮循。
④ 假如一直有新的对象过来,不断的做Minor GC,当幸存者区的From区和To区轮循大概15次时,假如To区还有存活的对象,再做一次Minor GC则会把存活的对象移入老年代。
⑤ 假如有一天老年代也占满了,则会触发Full GC再次回收无引用对象(有的GC会回收所有区,有的只会回收老年代,得看使用了哪种GC,后面会讲)。
三、实行引擎
实行引擎:读取运行时数据区的Java字节码并逐个实行。

解释实行字节码的方式:① JIT编译器;② 字节码解释器;③ mixed mode(前两种的混淆模式,JDK 8是采用这种,之前的我不知道)
JIT编译器(Just In Time,即时编译):一次性解释所有指令,第一次实行慢,后面实行快。(会缓存)
字节码解释器:逐行解释指令,每次实行必要重新解释,前几次可能比JIT编译器快,后面比它慢。(不缓存)

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?用户注册

x

天涯海角也要找到Ni:Java虚拟机——JVM

中发现Ni: Java虚拟机——JVM
中发现Ni: Java虚拟机——JVM
中发现Ni: Java虚拟机——JVM
中发现Ni: Java虚拟机——JVM
中发现Ni: Java虚拟机——JVM
中发现Ni: Java虚拟机——JVM
相关技术服务需求,请联系管理员和客服QQ:2753533861或QQ:619920289
您需要登录后才可以回帖 登录 | 用户注册

本版积分规则

帖子推荐:
客服咨询

QQ:2753533861

服务时间 9:00-22:00

快速回复 返回顶部 返回列表