JVM学习总结笔记2

JVM学习总结笔记2_第1张图片

内存模型 , 类加载机制 ,GC 是重点方面 . 性能调优部分更偏向应用 , 重点突出实践能力 . 编译器优化和执
行模式部分偏向于理论基础 , 重点掌握知识点 .需了解 内存模型 各部分作用 , 保存哪些数据 .
类加载 双亲委派加载机制 , 常用加载器分别加载哪种类型的类 .
GC 分代回收的思想和依据以及不同垃圾回收算法的回收思路和适合场景 .
性能调优 常有 JVM 优化参数作用 , 参数调优的依据 , 常用的 JVM 分析工具能分析哪些问题以及使用方法 . 执行模式 解释 / 编译 / 混合模式的优缺点 ,Java7 提供的分层编译技术 ,JIT 即时编译技术 ,OSR 栈上替换 ,C1/C2
编译器针对的场景 ,C2 针对的是 server 模式 , 优化更激进 . 新技术方面 Java10 graal 编译器
编译器优化 j avac 的编译过程 ,ast 抽象语法树 , 编译器优化和运行器优化 .
1 JVM 内存模型:
线程独占 : , 本地方法栈 , 程序计数器
线程共享 : , 方法区
2 、栈:
又称方法栈 , 线程私有的 , 线程执行方法是都会创建一个栈阵 , 用来存储局部变量表 , 操作栈 , 动态链接 , 方法
出口等信息 . 调用方法时执行入栈 , 方法返回式执行出栈 .
3 、本地方法栈
与栈类似 , 也是用来保存执行方法的信息 . 执行 Java 方法是使用栈 , 执行 Native 方法时使用本地方法栈 .
4 、程序计数器
保存着当前线程执行的字节码位置 , 每个线程工作时都有独立的计数器 , 只为执行 Java 方法服务 , 执行
Native 方法时 , 程序计数器为空 .
5 、堆
JVM 内存管理最大的一块 , 对被线程共享 , 目的是存放对象的实例 , 几乎所欲的对象实例都会放在这里 , 当堆
没有可用空间时 , 会抛出 OOM 异常 . 根据对象的存活周期不同 ,JVM 把对象进行分代管理 , 由垃圾回收器进行
垃圾的回收管理
6 、方法区:
又称非堆区 , 用于存储已被虚拟机加载的类信息 , 常量 , 静态变量 , 即时编译器优化后的代码等数据 .1.7 的永
久代和 1.8 的元空间都是方法区的一种实现
7 JVM 内存可见性

JVM学习总结笔记2_第2张图片

JMM 是定义程序中变量的访问规则 , 线程对于变量的操作只能在自己的工作内存中进行 , 而不能直接对主内存操作. 由于指令重排序 , 读写的顺序会被打乱 , 因此 JMM 需要提供原子性 , 可见性 , 有序性保证 .

JVM学习总结笔记2_第3张图片 

JVM学习总结笔记2_第4张图片

其中 验证 , 准备 , 解析 合称链接
加载 通过类的完全限定名 , 查找此类字节码文件 , 利用字节码文件创建 Class 对象
验证 确保 Class 文件符合当前虚拟机的要求 , 不会危害到虚拟机自身安全 . 准备 进行内存分配 , static 修饰的类变量分配内存 , 并设置初始值 (0 null). 不包含 fifinal 修饰的静态变量 ,
因为 fifinal 变量在编译时分配 .
解析 将常量池中的符号引用替换为直接引用的过程 . 直接引用为直接指向目标的指针或者相对偏移量等 .
初始化 主要完成静态块执行以及静态变量的赋值 . 先初始化父类 , 再初始化当前类 . 只有对类主动使用时才会初始化.
触发条件包括 , 创建类的实例时 , 访问类的静态方法或静态变量的时候 , 使用 Class.forName 反射类的时候 , 或者某个子类初始化的时候.
Java 自带的加载器加载的类 , 在虚拟机的生命周期中是不会被卸载的 , 只有用户自定义的加载器加载的类才可以被卸.
1 、加载机制 - 双亲委派模式

 

JVM学习总结笔记2_第5张图片

 双亲委派模式,即加载器加载类时先把请求委托给自己的父类加载器执行,直到顶层的启动类加载器.父类

加载器能够完成加载则成功返回 , 不能则子类加载器才自己尝试加载 .*
优点 :
1. 避免类的重复加载
2. 避免 Java 的核心 API 被篡改
2 、分代回收 分代回收基于两个事实 : 大部分对象很快就不使用了 , 还有一部分不会立即无用 , 但也不会持续很长时间 .

JVM学习总结笔记2_第6张图片

年轻代 -> 标记 - 复制
老年代 -> 标记 - 清除
3 、回收算法
a G1 算法
1.9 后默认的垃圾回收算法 , 特点保持高回收率的同时减少停顿 . 采用每次只清理一部分 , 而不是清理全部的增量式清理, 以保证停顿时间不会过长
其取消了年轻代与老年代的物理划分 , 但仍属于分代收集器 , 算法将堆分为若干个逻辑区域 (region), 一部分用作年轻代, 一部分用作老年代 , 还有用来存储巨型对象的分区 .
CMS 相同 , 会遍历所有对象 , 标记引用情况 , 清除对象后会对区域进行复制移动 , 以整合碎片空间 .
年轻代回收 :
并行复制采用复制算法 , 并行收集 , StopTheWorld.
老年代回收 :
会对年轻代一并回收
初始标记完成堆 root 对象的标记 , StopTheWorld.
并发标记 GC 线程和应用线程并发执行 .
最终标记完成三色标记周期 , StopTheWorld.
复制 / 清楚会优先对可回收空间加大的区域进行回收
b ZGC 算法
前面提供的高效垃圾回收算法 , 针对大堆内存设计 , 可以处理 TB 级别的堆 , 可以做到 10ms 以下的回收停顿时间.

JVM学习总结笔记2_第7张图片

着色指针
读屏障
并发处理
基于 region
内存压缩 ( 整理 ) roots 标记:标记 root 对象 , StopTheWorld.
并发标记:利用读屏障与应用线程一起运行标记 , 可能会发生 StopTheWorld.
清除会清理标记为不可用的对象 .
roots 重定位:是对存活的对象进行移动 , 以腾出大块内存空间 , 减少碎片产生 . 重定位最开始会
StopTheWorld, 却决于重定位集与对象总活动集的比例 .
并发重定位与并发标记类似 .

JVM学习总结笔记2_第8张图片

 JVM学习总结笔记2_第9张图片

线程私有区
1 、程序计数器
当同时进行的线程数超过 CPU 数或其内核数时,就要通过时间片轮询分派 CPU 的时间资源,不免发生线程切换。这时,每个线程就需要一个属于自己的计数器来记录下一条要运行的指令。如果执行的是JAVA 方法,计数器记录正在执行的java 字节码地址,如果执行的是 native 方法,则计数器为空。
2 、虚拟机栈
线程私有的,与线程在同一时间创建。管理 JAVA 方法执行的内存模型。每个方法执行时都会创建一个桢栈来存储方法的的变量表、操作数栈、动态链接方法、返回值、返回地址等信息。栈的大小决定了方法调用的可达深度(递归多少层次,或嵌套调用多少层其他方法,-Xss 参数可以设置虚拟机栈大小)。栈的大小可以是固定的,或者是动态扩展的。如果请求的栈深度大于最大可用深度,则抛出 stackOverflflowError;如果栈是可动态扩展的,但没有内存空间支持扩展,则抛出
OutofMemoryError
使用 jclasslib 工具可以查看 class 类文件的结构。下图为栈帧结构图:

JVM学习总结笔记2_第10张图片

本地方法栈
与虚拟机栈作用相似。但它不是为 Java 方法服务的,而是本地方法( C 语言)。由于规范对这块没有强制要求,不同虚拟机实现方法不同。
线程共享区
1 、方法区
线程共享的,用于存放被虚拟机加载的类的元数据信息,如常量、静态变量和即时编译器编译后的代码。若要分代,算是永久代(老年代),以前类大多“static” 的,很少被卸载或收集,现回收废弃常量和无用的类。其中运行时常量池存放编译生成的各种常量。(如果hotspot 虚拟机确定一个类的定义信息不会被使用,也会将其回收。回收的基本条件至少有:所有该类的实例被回收,而且装载该类的 ClassLoader被回收)
2 、堆
存放对象实例和数组,是垃圾回收的主要区域,分为新生代和老年代。刚创建的对象在新生代的 Eden 区中,经过GC 后进入新生代的 S0 区中,再经过 GC 进入新生代的 S1 区中, 15 GC 后仍存在就进入老年代。这是按照一种回收机制进行划分的,不是固定的。若堆的空间不够实例分配,则
OutOfMemoryError

JVM学习总结笔记2_第11张图片

 JVM学习总结笔记2_第12张图片

堆和栈的区别
栈是运行时单位,代表着逻辑,内含基本数据类型和堆中对象引用,所在区域连续,没有碎片;堆是存储单位,代表着数据,可被多个栈共享(包括成员中基本数据类型、引用和引用对象),所在区域不连续,会有碎片。
1 、功能不同
栈内存用来存储局部变量和方法调用,而堆内存用来存储 Java 中的对象。无论是成员变量,局部变量, 还是类变量,它们指向的对象都存储在堆内存中。
2 、共享性不同
Young Generation 即图中的 Eden + From Space s0 + To Space(s1)
Eden 存放新生的对象 Survivor Space 有两个,存放每次垃圾回收后存活的对象 (s0+s1)
Old Generation Tenured Generation 即图中的 Old Space 主要存放应用程序中生命周期长的存活对象栈内存是线程私有的。 堆内存是所有线程共有的。
3 、异常错误不同
如果栈内存或者堆内存不足都会抛出异常。
栈空间不足: java.lang.StackOverFlowError
堆空间不足: java.lang.OutOfMemoryError
4 、空间大小
栈的空间大小远远小于堆的。

 

什么时候会触发 FullGC
除直接调用 System.gc 外,触发 Full GC 执行的情况有如下四种。
1. 旧生代空间不足
旧生代空间只有在新生代对象转入及创建为大对象、大数组时才会出现不足的现象,当执行 Full GC 后空间仍然不足,则抛出如下错误:
java.lang.OutOfMemoryError: Java heap space
为避免以上两种状况引起的 FullGC ,调优时应尽量做到让对象在 Minor GC 阶段被回收、让对象在新生代多存活一段时间及不要创建过大的对象及数组。
2. Permanet Generation 空间满
PermanetGeneration 中存放的为一些 class 的信息等,当系统中要加载的类、反射的类和调用的方法较多时,Permanet Generation 可能会被占满,在未配置为采用 CMS GC 的情况下会执行 Full GC 。如果经过Full GC 仍然回收不了,那么 JVM 会抛出如下错误信息:
java.lang.OutOfMemoryError: PermGen space
为避免 Perm Gen 占满造成 Full GC 现象,可采用的方法为增大 Perm Gen 空间或转为使用 CMS GC
3. CMS GC 时出现 promotion failed concurrent mode failure
对于采用 CMS 进行旧生代 GC 的程序而言,尤其要注意 GC 日志中是否有 promotion failed concurrent mode failure两种状况,当这两种状况出现时可能会触发 Full GC
promotionfailed 是在进行 Minor GC 时, survivor space 放不下、对象只能放入旧生代,而此时旧生代也放不下造成的;concurrent mode failure 是在执行 CMS GC 的过程中同时有对象要放入旧生代,而此时旧生代空间不足造成的。
应对措施为:增大 survivorspace 、旧生代空间或调低触发并发 GC 的比率,但在 JDK 5.0+ 6.0+ 的版本中有可能会由于JDK bug29 导致 CMS remark 完毕后很久才触发 sweeping 动作。对于这种状况,通过设置-XX:CMSMaxAbortablePrecleanTime=5 (单位为 ms )来避免。
4. 统计得到的 Minor GC 晋升到旧生代的平均大小大于旧生代的剩余空间
这是一个较为复杂的触发情况, Hotspot 为了避免由于新生代对象晋升到旧生代导致旧生代空间不足的 现象,在进行Minor GC 时,做了一个判断,如果之前统计所得到的 Minor GC 晋升到旧生代的平均大大于旧生代的剩余空间,那么就直接触发Full GC
例如程序第一次触发 MinorGC 后,有 6MB 的对象晋升到旧生代,那么当下一次 Minor GC 发生时,首先检查旧生代的剩余空间是否大于6MB ,如果小于 6MB ,则执行 Full GC
当新生代采用 PSGC 时,方式稍有不同, PS GC 是在 Minor GC 后也会检查,例如上面的例子中第一次 Minor GC后, PS GC 会检查此时旧生代的剩余空间是否大于 6MB ,如小于,则触发对旧生代的回收。 除了以上4 种状况外,对于使用 RMI 来进行 RPC 或管理的 Sun JDK 应用而言,默认情况下会一小时执行一 次Full GC 。可通过在启动时通过 - java-Dsun.rmi.dgc.client.gcInterval=3600000 来设置 Full GC 执行的间隔时间或通过-XX:+ DisableExplicitGC 来禁止 RMI 调用 System.gc

对象分配规则
对象优先分配在 Eden 区,如果 Eden 区没有足够的空间时,虚拟机执行一次 Minor GC
大对象直接进入老年代(大对象是指需要大量连续内存空间的对象)。这样做的目的是避免在
Eden 区和两个 Survivor 区之间发生大量的内存拷贝(新生代采用复制算法收集内存)。
长期存活的对象进入老年代。虚拟机为每个对象定义了一个年龄计数器,如果对象经过了 1
Minor GC 那么对象会进入 Survivor 区,之后每经过一次 Minor GC 那么对象的年龄加 1 ,知道达到
阀值对象进入老年区。
动态判断对象的年龄。如果 Survivor 区中相同年龄的所有对象大小的总和大于 Survivor 空间的一
半,年龄大于或等于该年龄的对象可以直接进入老年代。
空间分配担保。每次进行 Minor GC 时, JVM 会计算 Survivor 区移至老年区的对象的平均大小,如
果这个值大于老年区的剩余值大小则进行一次 Full GC ,如果小于检查 HandlePromotionFailure
置,如果 true 则只进行 Monitor GC, 如果 false 则进行 Full GC
如何判断对象可以被回收?
判断对象是否存活一般有两种方式:
引用计数:每个对象有一个引用计数属性,新增一个引用时计数加 1 ,引用释放时计数减 1 ,计数
0 时可以回收。此方法简单,无法解决对象相互循环引用的问题。
可达性分析( Reachability Analysis ):从 GC Roots 开始向下搜索,搜索所走过的路径称为引用
链。当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是不可用的,不可达对象。
JVM 的永久代中会发生垃圾回收么?
垃圾回收不会发生在永久代,如果永久代满了或者是超过了临界值,会触发完全垃圾回收 (Full GC) 。如 果你仔细查看垃圾收集器的输出信息,就会发现永久代也是被回收的。这就是为什么正确的永久代大小 对避免Full GC 是非常重要的原因。请参考下 Java8 :从永久代到元数据区 ( 注: Java8 中已经移除了永久 代,新加了一个叫做元数据区的native 内存区 )
垃圾收集算法
GC 最基础的算法有三种: 标记 - 清除算法、复制算法、标记 - 压缩算法,我们常用的垃圾回收器一般都采用分代收集算法。
标记 - 清除算法, 标记 - 清除 Mark-Sweep )算法,如它的名字一样,算法分为 标记 清除
个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。
复制算法, 复制 Copying )的收集算法,它将可用内存按容量划分为大小相等的两块,每次只
使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已
使用过的内存空间一次清理掉。
标记 - 压缩算法,标记过程仍然与 标记 - 清除 算法一样,但后续步骤不是直接对可回收对象进行清
理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存
分代收集算法, 分代收集 Generational Collection )算法,把 Java 堆分为新生代和老年代,这
样就可以根据各个年代的特点采用最适当的收集算法。
调优命令有哪些?
Sun JDK监控和故障处理命令有jps jstat jmap jhat jstack jinfo
jpsJVM Process Status Tool,显示指定系统内所有的HotSpot虚拟机进程。
jstatJVM statistics Monitoring是用于监视虚拟机运行时状态信息的命令,它可以显示出虚拟机 进程中的类装载、内存、垃圾收集、JIT编译等运行数据。
jmapJVM Memory Map命令用于生成heap dump文件
jhatJVM Heap Analysis Tool命令是与jmap搭配使用,用来分析jmap生成的dumpjhat内置了 一个微型的HTTP/HTML服务器,生成dump的分析结果后,可以在浏览器中查看
jstack,用于生成java虚拟机当前时刻的线程快照。jinfoJVM Confifiguration info 这个命令作用是实时查看和调整虚拟机运行参数。
调优工具
常用调优工具分为两类,jdk自带监控工具:jconsolejvisualvm,第三方有:MAT(Memory Analyzer Tool)、GChisto
jconsoleJava Monitoring and Management Console是从java5开始,在JDK中自带的java监控 和管理控制台,用于对JVM中内存,线程和类等的监控
jvisualvmjdk自带全能工具,可以分析内存快照、线程快照;监控内存变化、GC变化等。
MATMemory Analyzer Tool,一个基于Eclipse的内存分析工具,是一个快速、功能丰富的Java heap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗 GChisto,一款专业分析gc日志的工具
Minor GC Full GC 分别在什么时候发生?
新生代内存不够用时候发生 MGC 也叫 YGC JVM 内存不够的时候发生 FGC
你知道哪些 JVM 性能调优
设定堆内存大小
-Xmx :堆内存最大限制。
设定新生代大小。 新生代不宜太小,否则会有大量对象涌入老年代
-XX:NewSize :新生代大小
-XX:NewRatio 新生代和老生代占比
-XX:SurvivorRatio :伊甸园空间和幸存者空间的占比
设定垃圾回收器 年轻代用 -XX:+UseParNewGC 年老代用 -XX:+UseConcMarkSweepGC
JVM 内存分哪几个区,每个区的作用是什么 ?
Java 虚拟机主要分为以下一个区 :
方法区:
1. 有时候也成为永久代,在该区内很少发生垃圾回收,但是并不代表不发生 GC ,在这里进行的
GC 主要是对方法区里的常量池和对类 型的卸载
2. 方法区主要用来存储已被虚拟机加载的类的信息、常量、静态变量和即时编译器编译后的代
码等数据。
3. 该区域是被线程共享的。
4. 方法区里有一个运行时常量池,用于存放静态编译产生的字面量和符号引用。该常量池具有
动态性,也就是说常量并不一定是编 译时确定,运行时生成的常量也会存在这个常量池中。
虚拟机栈 :
1. 虚拟机栈也就是我们平常所称的栈内存 , 它为 java 方法服务,每个方法在执行的时候都会创建
一个栈帧,用于存储局部变量表、操 作数栈、动态链接和方法出口等信息。
2. 虚拟机栈是线程私有的,它的生命周期与线程相同。 3. 局部变量表里存储的是基本数据类型、 returnAddress 类型(指向一条字节码指令的地址)
和对象引用,这个对象引用有可能是指 向对象起始地址的一个指针,也有可能是代表对象的
句柄或者与对象相关联的位置。局部变量所需的内存空间在编译器间确定
4. 操作数栈的作用主要用来存储运算结果以及运算的操作数,它不同于局部变量表通过索引来
访问,而是压栈和出栈的方式
5. 每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持
方法调用过程中的动态连接 . 动态链接就是将常量池中的符号引用在运行期转化为直接引用。
本地方法栈 本地方法栈和虚拟机栈类似,只不过本地方法栈为Native 方法服务。
堆 Java堆是所有线程所共享的一块内存,在虚拟机启动时创建,几乎所有的对象实例都在这里
创建,因此该区域经常发生垃圾回收操作 。
程序计数器 内存空间小,字节码解释器工作时通过改变这个计数值可以选取下一条需要执行的字节码指 令,分支、循环、跳转、异常处理和线程恢复等功能都需要依赖这个计数器完成。该内存区域是唯一一个java 虚拟机规范没有规定任何 OOM 情况的区域。
简述 Java 垃圾回收机制 ?
Java 中,程序员是不需要显示的去释放一个对象的内存的,而是由虚拟机自行执行。在 JVM 中,有一 个垃圾回收线程,它是低优先级的,在正常情况下是不会执行的,只有在虚拟机空闲或者当前堆内存不
足时,才会触发执行,扫面那些没有被任何引用的对象,并将它们添加到要回收的集合中,进行回收。
什么是类加载器,类加载器有哪些 ?
实现通过类的权限定名获取该类的二进制字节流的代码块叫做类加载器。
主要有一下四种类加载器 :
1. 启动类加载器 (Bootstrap ClassLoader) 用来加载 java 核心类库,无法被 java 程序直接引用。
2. 扩展类加载器 (extensions class loader): 它用来加载 Java 的扩展库。 Java 虚拟机的实现会提供一
个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
3. 系统类加载器( system class loader ):它根据 Java 应用的类路径( CLASSPATH )来加载 Java
类。一般来说, Java 应用的类都是由它来完成加载的。可以通过
ClassLoader.getSystemClassLoader() 来获取它。
4. 用户自定义类加载器,通过继承 java.lang.ClassLoader 类的方式实现。
你有没有遇到过 OutOfMemory 问题?你是怎么来处理这个问
题的?处理 过程中有哪些收获?
常见的原因
内存加载的数据量太大:一次性从数据库取太多数据;
集合类中有对对象的引用,使用后未清空,GC不能进行回收;
代码中存在循环产生过多的重复对象;
启动参数堆内存值小。
JDK 1.8 之后 Perm Space 有哪些变动 ? MetaSpace ⼤⼩默认是
⽆限的么 ? 还是你们会通过什么⽅式来指定⼤⼩ ? JDK 1.8 后用元空间替代了 Perm Space ;字符串常量存放到堆内存中。
MetaSpace 大小默认没有限制,一般根据系统内存的大小。 JVM 会动态改变此值。
-XX:MetaspaceSize :分配给类元数据空间(以字节计)的初始大小( Oracle 逻辑存储上的初始高水位,the initial high-water-mark )。此值为估计值, MetaspaceSize 的值设置的过大会延长垃圾回收时间。垃圾回收过后,引起下一次垃圾回收的类元数据空间的大小可能会变大。
-XX:MaxMetaspaceSize :分配给类元数据空间的最大值,超过此值就会触发 Full GC ,此值默认没有限制,但应取决于系统内存的大小。JVM 会动态地改变此值。

JVM学习总结笔记2_第13张图片

如何启动系统的时候设置 JVM 的启动参数

JVM学习总结笔记2_第14张图片

 

 

你可能感兴趣的:(JVM,小白学java,学习)