一、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. .NET 框架设计 - 模式、配置、工具 Demo 搜索

    ps:http://www.ituring.com.cn/article/195770 看完第二章,我也忍了!但是随着第三章“副本”的开启,我的阅读速度开始慢慢的减缓,虽然对其中每一个程序模式的理论也 ...

  2. 每天敲一点code

    下面这段代码摘自 <C#并发编程经典实例> 并行LINQ static IEnumerable<int> MultiplyBy2(IEnumerable<int> ...

  3. NOI2.5 8465:马走日

    描述 马在中国象棋以日字形规则移动. 请编写一段程序,给定n*m大小的棋盘,以及马的初始位置(x,y),要求不能重复经过棋盘上的同一个点,计算马可以有多少途径遍历棋盘上的所有点. 输入 第一行为整数T ...

  4. Visual studio之C#的一些常见问题01switch case常量

    switch() {case CONST: break;}语句中,case后面的常量表达方法在C/C++中,switch() {case CONST: break;}语句中的CONST常常使用宏定义来 ...

  5. 本地python3环境下运行报错CV2的问题

    如上图,执行脚本后,报找不到指定的模块的错误 解决方法: 1.import cv2提示"Dll load failed:找不到指定的模块" 解决方法:那卸载掉opencv-cont ...

  6. 02--java--环境搭建

    第一步,下载JDK 去ORACLE官网http://www.oracle.com下载 有安装版和绿色版,安装版一路下一步,绿色版解压缩压缩包就行了 安装版直接自动配置环境变量,绿色版需要自己配置环境变 ...

  7. ReactNative---android系统中Image组件无默认图片问题

    react native的Image组件通过网络地址加载图片的时候,若加载失败iOS有默认图片等属性,但安卓没有:但可以通过其他方式来实现: {Platform.OS == 'android'?< ...

  8. idea中使用Live Template自动生成方法所有参数打印

    一 新建模板 二 设置代码模板 三 设置变量 表达式是支持groovy脚本的,所以这里写一个groovy脚本,生成给定格式的日志字符串,methodParameters()是idea内置的函数,获取方 ...

  9. 生成HTML测试报告表格

    #生成HTML测试报告 #-*- coding:utf-8 -*- from selenium import webdriver from selenium.webdriver.common.by i ...

  10. ELK同步kafka带有key的Message

    需求 kafka中的message带有key,带有相同key值的message后入kafka的意味着更新message,message值为null则意味着删除message. 用logstash来同步 ...