java程序员必备的JVM学习
文章目录
- 前言
- JVM概述
-
- 为什么要学习jvm
- 虚拟机
- JVM作用
- JVM位置
- JVM整体组成可分为以下四个部分
- 各组成部分用途
- java代码的执行流程
- JVM架构模型
- JVM类加载
-
- 类加载子系统
- 类加载的角色
- 类加载过程
-
- 加载
- 链接
- 初始化
-
- 类什么时候初始化/li>
- 类的初始化顺序
- 类加载器分类
- 双亲委派机制
-
- 工作原理
- 优点
- 类的主动使用/被动使用
- JVM运行时数据区
-
- 运行时数据区组成概述
- 程序计数器(Program Counter Register)
- Java 虚拟机栈(Java Virtual Machine Stacks)
-
- 背景
- 分清栈和堆
- Java 虚拟机栈是什么
- 作用
- 栈的特点
- 栈中出现的异常
- 栈中存储什么/li>
- 栈的运行原理
- 栈帧的内部结构
- 本地方法栈
- Java 堆内存
-
- 概述
- 堆内存区域划分
- 为什么分区(代)
- 对象创建内存分配过程
- 新生区与老年区配置比例
- 分代收集思想 Minor GC、Major GC、Full GC
- TLAB 机制
- 堆空间的参数设置
- 字符串常量池
- 方法区
-
- 概述
- 方法区大小设置
- 方法区的内部结构
- 方法区的垃圾回收
- 本地方法接口
-
- 什么是本地方法
- 为什么要使用 Native Method
- 执行引擎
-
- 概述
- 什么是解释器么是JIT编译器
- 为什么Java是半编译半解释型语言/li>
- 垃圾回收
-
- 垃圾回收概述
-
- 概述
- 什么是垃圾
- 为什么需要GC/li>
- 早期垃圾回收
- Java 垃圾回收机制
-
- 自动内存管理
- 应该关心哪些区域的回收/li>
- 垃圾回收相关算法
-
- 垃圾标记阶段算法
-
- 标记阶段目的
- 引用计数算法
- 可达性分析算法
- 对象的 finalization 机制
- 生存还是死亡
- 垃圾回收阶段算法
-
- 标记-清除算法
- 复制算法
- 标记-压缩算法
- 垃圾回收算法小结
- 分代收集
- 垃圾回收相关概念
-
- System.gc() 的理解
- 内存溢出与内存泄漏
-
- 内存溢出
- 内存泄漏
- 常见例子
- Stop the World
- 对象引用
-
- 概述
- 强引用
- 软引用(Soft Reference):内存不足即回收
- 弱引用(Weak Reference)发现即回收
- 虚引用(Phantom Reference):对象回收跟踪
- 垃圾回收器
-
- 概述
- 垃圾回收器分类
- 性能指标
- HotSpot 垃圾回收器
-
- 1.Serial 垃圾收集器(单线程)
- 2.Serial Old 垃圾收集器(单线程)
- 3.ParNew 垃圾收集器(多线程)
- 4.Parallel Scavenge 垃圾收集器(多线程)
- 5.Parallel Old 垃圾收集器(多线程)
- 6. CMS 回收器(低延迟)
- 7.G1(Garbage First)回收器(区域划分代式)
前言
JVM的学习毋庸置疑是非常重要的,在面试中也是非常常见 本次的内容比较多,如果您能耐心看完,我想一定能对你有帮助
JVM概述
为什么要学习jvm
JVM是中高级程序员必备技能,可以对项目进行管理,对性能进行调优
虚拟机
? 所谓虚拟机,就是一台虚拟机。它是一款软件,用来执行一系列虚拟计算机指令。大体上,虚拟机可以分为系统虚拟机和程序虚拟机。
? 大名鼎鼎的VMware就属于虚拟机,它是完全对物理计算机的仿真,提供了一个可运行完整操作系统的软件平台。程序虚拟机典型的代表就是java虚拟机了,它专门为执行某个单个计算机程序而设计。在java虚拟机中执行的指令我们称为java字节码指令。
? java虚拟机是一种执行java字节码文件的虚拟计算机,它拥有独立的 运行机制。
? java技术的核心就是java虚拟机,因为所有的java程序都运行在java虚拟机内部。
JVM作用
JVM位置
JVM整体组成可分为以下四个部分
1、类加载器(ClassLoader)
2、运行时数据区(Runtime Data Area)
3、执行引擎(Execution Engine)
4、本地库接口(Native Interface)
各组成部分用途
程序在执行之前先要把java代码转换成字节码(class文件),JVM首先需要把字节码通过一定的方式类加载器(ClassLoader)把文件加载到内存中的运行时数据区(Runtime Data Area),而字节码文件是JVM的一套指令集规范,并不能直接交给底层操作系统去执行。因此需要特定的命令解析器**执行引擎(Execution Engine)将字节码翻译成底层操作系统指令再交由CPU去执行,而这个过程中需要调用其他语言的接口本地库接口(Native Interface)来实现整个程序的功能,这就是这4个组成部分的职责与功能。
而我们通常所说的JVM组成指的是运行时数据区(Runtime Data Area),因为通常需要程序员吊事分析的区域就是”运行时数据区“,或者更具体的来说就是”运行时数据区“里面的Heap(堆)模块。
java代码的执行流程
java编译器编译过程中,任何一个结点执行失败就会造成编译失败。虽然各个平台的java虚拟机内部实现细节不尽相同,但是它们执行的字节码内容确实一样的。
JVM主要任务就是负责将字节码装载到其内部,解释/编译为对应平台上的机器指令执行。JVM使用类加载器(ClassLoader)装载class文件。
类加载完成后,会进行字节码校验,字节码校验通过之后JVM解释器会把字节码翻译成机器码交由操作系统执行。
但不是所有的代码都是解释执行,JVM对此作了优化,比如HotSpot虚拟机,它本身提供了JIT(Just In Time)编译器。
JVM架构模型
java编译器输入的指令流基本上是一种基于栈的指令集架构,另一种指令集架构是基于寄存器的指令集架构。
这两种架构之间的区别:
基于栈式架构的特点
? 设计和实现更简单,适用于资源受限的系统。
? 使用零地址指令方式分配,其执行过程依赖于操作栈,指令集更小,编译器容易实现。
基于寄存器式架构特点:
? 指令完全依赖于硬件,可移植性差。
? 性能优秀,执行更高效。
? 完成一项操作使用的指令更少。
类加载器子系统负责从文件系统或者 络中加载 class 文件。 classLoader 只
负责 class 文件的加载,至于它是否可以运行,则由 Execution Engine 决定。
加载的类信息存放于一块称为方法区(元空间)的内存空间。
类加载的角色
加载
? 根据类的地址,从硬盘上读取类的信息,
? 将信息读入到方法区,生成Class类的对象
链接
验证:检验被加载的类是否有正确的内部结构,并和其他类协调一致;
? 对文件格式的验证:class 文件在文件开头有特定的文件标识(字节
码文件都以 CA FE BA BE 标识开头);主,次版本 是否在当前 java 虚拟机接收
范围内。
? 对元数据的验证:对字节码描述的信息进行语义分析,以保证其描述的信息符合java 语言规范的要求,例如这个类是否有父类;是否继承浏览不允许被继承的类 (final 修饰的类)…
准备:准备阶段则负责为类的静态属性分配内存,并设置默认初始值;
? 不包含用 final 修饰的 static 常量,在编译时进行初始化
例如: public static int value = 123;
? value 在准备阶段后的初始值是 0,而不是 123。
解析:将类的二进制数据中的符 引用替换成直接引用(符 引用是 Class 文
件的逻辑符 ,直接引用指向的方法区中某一个地址)。
初始化
类什么时候初始化/h4>
1 )创建类的实例,也就是 new一个对象
2)访问某个类或接口的静态变量,或者对该静态变量赋值
3)调用类的静态方法
4)反射(Class.forName(“”))
5)初始化一个类的子类(会首先初始化子类的父类)
类的初始化顺序
对 static 修饰的变量或语句块进行赋值.
如果同时包含多个静态变量和静态代码块,则按照自上而下的顺序依次执行。
如果初始化一个类的时候,其父类尚未初始化,则优先初始化其父类。
顺序是:父类 static –> 子类 static –> 父类构造方法- -> 子类构造方法
类加载器分类
站在 JVM 的角度看,类加载器可以分为两种:
- 引导类加载器(启动类加载器 Bootstrap ClassLoader). (java语言写的)
- 其他所有类加载器,这些类加载器由 java 语言实现,独立存在于虚拟机外部,并且全部继承自抽象类 java.lang.ClassLoader。(其他语言写的)
站在 java 开发人员的角度来看,类加载器就应当划分得更细致一些。自 JDK1.2 以来 java 一直保持者三层类加载器。
用户自定义类加载器
例如Tomcat
双亲委派机制
Java 虚拟机对 class 文件采用的是按需加载的方式,也就是说当需要该类时才会 将它的 class 文件加载到内存中生成 class 对象.而且加载某个类的 class 文件时,Java 虚拟机采用的是双亲委派模式,即把请求交由父类处理,它是一种任务委 派模式.
Java 虚拟机定义了序运行期间会使用到的运行数据区,其中有一些会随着虚拟机启动而创建,随着虚拟机退出而销毁.另外一些则是与线程一一对应的.这些与 线程对应的区域会随着线程开始和结束而创建销毁.
如图:红色的为多个线程共享,灰色的为单个线程私有的,即线程间共享:堆,方法区.
线程私有:程序计数器,栈,本地方法栈
栈的特点
栈是一种快速有效的分配存储方式,访问速度仅次于程序计数器。
JVM 直接对 java 栈的操作只有两个:1、调用方法,进栈 2、执行结束后出栈
对于栈来说不存在垃圾回收问题.
不同线程中所包含的栈帧(方法)是不允许存在相互引用的,即不可能在一个栈中 引用另一个线程的栈帧(方法).
如果当前方法调用了其他方法,方法返回之际,当前栈帧会传回此方法的执行结果给前一个栈帧,接着虚拟机会丢弃当前栈帧,使得前一个栈帧重新成为当前栈帧.Java 方法有两种返回的方式,一种是正常的函数返回,使用 return 指令,另一种是 抛出异常.不管哪种方式,都会导致栈帧被弹出.
栈帧的内部结构
局部变量表、操作数栈、动态链接、方法返回地址
局部变量表
? 局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局 部变量。对于基本数据类型的变量,则直接存储它的值,对于引用类型的变量,则存的是指向对象的引用。
操作数栈(Operand Stack)(或表达式栈)
? 栈最典型的一个应用就是用来对表达式求值。在一个线程执行方法的过程中,实际上就是不断执行语句的过程,而归根到底就是进行计算的过程。因此可以这么说,程序中的所有计算过程都是在借助于操作数栈来完成的。
动态链接(Dynamic Linking) (或指向运行时常量池的方法引用)
? 因为在方法执行的过程中有可能需要用到类中的常量,所以必须要有一个引用指向运行时常量。
方法返回地址(Retuen Address)(或方法正常退出或者异常退出的定义)
? 当一个方法执行完毕之后,要返回之前调用它的地方,因此在栈帧中必须保 存一个方法返回地址。
-
一个 JVM 实例只存在一个堆内存,堆也是 Java 内存管理的核心区域
-
Java 堆区在 JVM 启动时的时候即被创建,其空间大小也就确定了,是 JVM 管理的最大一块内存空间.
-
堆内存的大小是可以调节.
? 例如: -Xms:10m(堆起始大小) -Xmx:30m(堆最大内存大小)
? 一般情况可以将起始值和最大值设置为一致,这样会减少垃圾回收之后堆内存重新分配大小的次数,提高效率.
-
《Java 虚拟机规范》规定,堆可以处于物理上不连续的内存空间中,但逻辑上它应该被视为连续的.
-
所有的线程共享 Java 堆,在这里还可以划分线程私有的缓冲区.
-
《Java 虚拟机规范》中对 Java 堆的描述是:所有的对象实例都应当在运行时分配在堆上.
-
在方法结束后,堆中的对象不会马上被移除,仅仅在垃圾收集的时候才会被移除.
-
堆是 GC(Garbage Collection,垃圾收集器)执行垃圾回收的重点区域.
堆内存区域划分
Java8 及之后堆内存分为:新生区(新生代)+老年区(老年代)
新生区分为 Eden(伊甸园)区和 Survivor(幸存者)区

为什么分区(代)
将对象根据存活概率进行分类,对存活时间长的对象,放到固定区,从而减少扫 描垃圾时间及 GC 频率。针对分类进行不同的垃圾回收算法,对算法扬长避短。
对象创建内存分配过程
为新对象分配内存是一件非常严谨和复杂的任务,JVM 的设计者们不仅需要考虑内存如何分配,在哪分配等问题,并且由于内存分配算法与内存回收算法密切相关,所以还需要考虑 GC 执行完内存回收后是否会在内存空间中产生内存碎片
-
new 的新对象先放到伊甸园区,此区大小有限制.
-
当伊甸园的空间填满时,程序又需要创建对象时,JVM 的垃圾回收器将对伊甸园区进行垃圾回收(Minor GC),将伊甸园区中的不再被引用的对象进行销毁.再加载新的对象放到伊甸园区.
-
然后将伊甸园区中的剩余对象移动到幸存者 0 区.
-
如果再次出发垃圾回收,此时上次幸存下来存放到幸存者 0 区的对象,如果没有回收, 就会被放到幸存者 1 区,每次
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!