一、JVM整体架构

1、JVM(Java虚拟机):指以软件的方式模拟具有完整硬件系统功能、运行在一个完全隔离环境中的完整计算机系统,是物理机的软件实现。常用的虚拟机有VMWare、Virtual Box、Java Virtual Machine。

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

  • 类加载子系统 (即类加载器 / ClassLoader)
  • 运行时数据区(即内存结构 / 内存模型 / 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没有永久代,不需要配置永久代。

元数据区的动态扩展:默认-XX:MetaspaceSize值为21MB的高水位线。一旦触及则Full GC将被触发并卸载没有用的类(类对应的类加载器不再存活),然后高水位线将会充值。新的高水位线的值取决于GC后释放的元空间。如果释放的空间少,这个高水位线则上升。如果释放空间过多,则高水位线下降。

(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区占满时(或者即使没占满,当幸存者区的From区和To区轮循15次时(默认15),如果To区还有存活的对象),再做一次Minor GC则会把存活的对象移入老年代。

⑤ 如果有一天老年代也占满了,则会触发Full GC再次回收无引用对象(Full GC 是清理整个堆空间—包括年轻代和永久代)。

三、JVM执行引擎

JVM执行引擎:读取运行时数据区的Java字节码并逐个执行。

解释执行字节码的方式:① JIT编译器;② 字节码解释器;③ mixed mode(前两种的混合模式,JDK 8是采用这种,之前的我不知道)

JIT编译器(Just In Time,即时编译):一次性解释所有指令,第一次执行慢,后面执行快。(会缓存)

字节码解释器:逐行解释指令,每次执行需要重新解释,前几次可能比JIT编译器快,后面比它慢。(不缓存)

Java虚拟机——JVM的更多相关文章

  1. Java虚拟机JVM学习07 类的卸载机制

    Java虚拟机JVM学习07 类的卸载机制 类的生命周期 当Sample类被加载.连接和初始化后,它的生命周期就开始了. 当代表Sample类的Class对象不再被引用,即不可触及时,Class对象就 ...

  2. Java虚拟机JVM学习06 自定义类加载器 父委托机制和命名空间的再讨论

    Java虚拟机JVM学习06 自定义类加载器 父委托机制和命名空间的再讨论 创建用户自定义的类加载器 要创建用户自定义的类加载器,只需要扩展java.lang.ClassLoader类,然后覆盖它的f ...

  3. Java虚拟机JVM学习05 类加载器的父委托机制

    Java虚拟机JVM学习05 类加载器的父委托机制 类加载器 类加载器用来把类加载到Java虚拟机中. 类加载器的类型 有两种类型的类加载器: 1.JVM自带的加载器: 根类加载器(Bootstrap ...

  4. Java虚拟机JVM学习04 类的初始化

    Java虚拟机JVM学习04 类的初始化 类的初始化 在初始化阶段,Java虚拟机执行类的初始化语句,为类的静态变量赋予初始值. 在程序中,静态变量的初始化有两种途径: 1.在静态变量的声明处进行初始 ...

  5. Java虚拟机JVM学习03 连接过程:验证、准备、解析

    Java虚拟机JVM学习03 连接过程:验证.准备.解析 类被加载后,就进入连接阶段. 连接就是将已经读入到内存的类的二进制数据合并到虚拟机的运行时环境中去. 连接阶段三个步骤:验证.准备和解析. 类 ...

  6. Java虚拟机JVM学习02 类的加载概述

    Java虚拟机JVM学习02 类的加载概述 类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对 ...

  7. Java虚拟机JVM学习01 流程概述

    Java虚拟机JVM学习01 流程概述 Java虚拟机与程序的生命周期 一个运行时的Java虚拟机(JVM)负责运行一个Java程序. 当启动一个Java程序时,一个虚拟机实例诞生:当程序关闭退出,这 ...

  8. 深入解析java虚拟机-jvm运行机制

    转自oschina 一:JVM基础概念 JVM(Java虚拟机)一种用于计算设备的规范,可用不同的方式(软件或硬件)加以实现.编译虚拟机的指令集与编译微处理器的指令集非常类似.Java虚拟机包括一套字 ...

  9. java虚拟机 jvm 出入java栈 栈空间内存分配

    java栈空间是一块线程私有的内存空间,java堆和程序数据密切相关,那么java栈就是和线程执行密切相关.线程最基本的执行行为就是函数的调用.每次函数调用其实是通过java栈传递数据的. 数据结构中 ...

  10. Java虚拟机JVM相关知识整理

    Java虚拟机JVM的作用: Java源文件(.java)通过编译器编译成.class文件,.class文件通过JVM中的解释器解释成特定机器上的机器代码,从而实现Java语言的跨平台. JVM的体系 ...

随机推荐

  1. Docker 安装 Elasticsearch+kibana

    1 下载镜像 docker pull elasticsearch:7.4.1 docker pull kibana:7.4.1 拉取的镜像如下: 2 创建network 创建一个网络,名字任意取,使得 ...

  2. java进阶视频分享

    更多资源和教程请关注公众号:非科班的科班. 如果觉得我写的还可以请给个赞,谢谢大家,你的鼓励是我创作的动力 课程目录介绍 01.开班仪式02.并发编程专题之多线程基础03.并发编程专题之Java内存模 ...

  3. echats 的使用

    第一步在我们的电脑上百度搜索echarts,点击进去,如下图所示: 2 第二步进去之后,点击下载,选择要下载的echarts版本,一般选择源代码,如下图所示: 3 第三步下载完成之后,我们也可以来使用 ...

  4. 程序的健壮性Robustness

    所谓的程序健壮性是指处理异常的能力,在异常中能够独立处理异常,并且把正确的答案输出. 例如: 有一个程序能够下载一个文件到指定的路径,但是这个路径是不存在的,因此程序必须要处理这个情况. 例1:下面的 ...

  5. AS中使用真机调试时出现解析错误的问题

    时间:2019/12/8 今天使用usb调试程序时手机上出现了解析错误的问题,其实这个问题很简单,主要可能是你想要调试的程序的最低版本号大于你手机的安卓版本号的原因,只需要修改下面这个地方: buil ...

  6. Unreal Engine 4 蓝图完全学习教程(二)—— 初步尝试

    本篇尝试使用蓝图.蓝图是使用专门的编辑器进行编程. Ⅰ.3类蓝图 ①关卡蓝图:前面提到过,关卡是指在UE中制成的游戏场景.关卡蓝图是用于制作当前游戏场景的程序.在UE中进行编程就是在创建关卡蓝图. ② ...

  7. selenium 调用JavaScript代码

    selenium 调用JavaScript代码 调用JavaScript方法有两种: execute_script(): 方法解释:是同步方法,用它执行js代码会阻塞主线程执行,直到js代码执行完毕. ...

  8. Java中的8种基本数据类型

    JAVA中的8种基本数据类型:byte short int long float double char boolean 特别说明: 1)char类型占2个字节,可以表示汉字.汉字和英文字符都占2个字 ...

  9. yukongDSRM账户安全防护

    一.DSRM简介 1.DSRM(Diretcory Service Restore Mode,目录服务恢复模式)是windows域环境中域控制器的安全模式启动选项.域控制器的本地管理员账户也就是DSR ...

  10. Ambari HDP 下 SPARK2 与 Phoenix 整合

    1.环境说明 操作系统 CentOS Linux release 7.4.1708 (Core) Ambari 2.6.x HDP 2.6.3.0 Spark 2.x Phoenix 4.10.0-H ...