当前位置:   article > 正文

JVM 相关知识整理

JVM 相关知识整理

前言

  如果您觉得有用的话,记得给博主点个赞,评论,收藏一键三连啊,写作不易啊^ _ ^。
  而且听说点赞的人每天的运气都不会太差,实在白嫖的话,那欢迎常来啊!!!


JVM 相关知识整理

在这里插入图片描述

1. 新生代和老年代

老年代比新生代生命周期长。
新生代与老年代空间默认比例 1:2:JVM 调参数,XX:NewRatio=2,表示新生代占 1,老年代占 2,新生代占整个堆的 1/3。
HotSpot 中,Eden 空间和另外两个 Survivor 空间缺省所占的比例是:8:1:1。
几乎所有的 Java 对象都是在 Eden 区被 new 出来的,Eden 放不了的大对象,就直接进入老年代了。

2. 对象的分配过程

(1)new 的对象先放在 Eden 区,大小有限制;
(2)如果创建新对象时,Eden 空间填满了,就会触发 Minor GC,将 Eden 不再被其他对象引用的对象进行销毁,再加载新的对象放到 Eden 区,特别注意的是 Survivor 区满了是不会触发 Minor GC 的,而是 Eden 空间填满了,Minor GC 才顺便清理 Survivor 区。;
(3)将 Eden 中剩余的对象移到 Survivor0 区;
(4)再次触发垃圾回收,此时上次 Survivor 下来的,放在 Survivor0 区的,如果没有回收,就会放到 Survivor1 区;
(5)再次经历垃圾回收,又会将幸存者重新放回 Survivor0 区,依次类推;
(6)默认是 15 次的循环,超过 15 次,则会将幸存者区幸存下来的转去老年区 jvm 参数设置次数 : -XX:MaxTenuringThreshold=N 进行设置
(7)频繁在新生区收集,很少在养老区收集,几乎不在永久区/元空间搜集

3. Full GC /Major GC 触发条件

显示调用System.gc(),老年代的空间不够,方法区的空间不够等都会触发 Full GC,同时对新生代和老年代回收,FUll GC 的 STW 的时间最长,应该要避免。
在出现 Major GC 之前,会先触发 Minor GC,如果老年代的空间还是不够就会触发 Major GC,STW 的时间长于 Minor GC。

4. 逃逸分析

随着 JIT 编译期的发展与逃逸分析技术逐渐成熟,栈上分配,标量替换优化技术将会导致一些变化,所有的对象都分配到堆上也渐渐变得不那么"绝对"了。

这是一种可以有效减少 Java 内存堆分配压力的分析算法,通过逃逸分析,Java Hotspot 编译器能够分析出一个新的对象的引用的使用范围从而决定是否要将这个对象分配到堆上,当一个对象在方法中被定义后,它可能被外部方法所引用,如作为调用参数传递到其他地方中,称为方法逃逸,再如赋值给类变量或可以在其他线程中访问的实例变量,称为线程逃逸。
在编译期间,如果 JIT 经过逃逸分析,发现有些对象没有逃逸出方法,那么有可能堆内存分配会被优化成栈内存分配。
jvm 参数设置,-XX:+DoEscapeAnalysis :开启逃逸分析 ,-XX:-DoEscapeAnalysis : 关闭逃逸分析。
从 jdk 1.7 开始已经默认开始逃逸分析。

4.1.示例

在标量替换过程中
标量:表示不可分解的量,如java 的基本数据类型就是标量,反之就是可以分解的量,称为聚合量,而在 JAVA 中对象就是可以被进一步分解的聚合量。
替换过程,通过逃逸分析确定该对象不会被外部访问,并且对象可以被进一步分解时,JVM 不会创建该对象,而会将该对象成员变量分解若干个被这个方法使用的成员变量所代替。这些代替的成员变量在栈帧或寄存器上分配空间。

4.2. 使用逃逸分析,编译器可以对代码做如下优化

(1)同步省略:如果一个对象被发现只能从一个线程被访问到,那么对于这个对象的操作可以不考虑同步。
(2)将堆分配转化为栈分配:如果一个对象在子程序中被分配,要使指向该对象的指针永远不会逃逸,对象可能是栈分配的候选,而不是堆分配。
(3)分离对象或标量替换:有的对象可能不需要作为一个连续的内存结构存在也可以被访问到,那么对象的部分(或全部)可以不存储在内存,而是存储在 CPU 寄存器中。

5. 对象的内存分配

对象的内存分配,就是在堆上分配,对象主要分配在新生代的 Eden 区上,少数情况下可能直接分配在老年代,分配规则不固定,取决于当前使用的垃圾收集器组合以及相关的参数配置。

注:对象优先分布在Eden 区,大多数情况下,对象在新生代 Eden 区中分配。当 Eden 区没有足够空间进行分配时,虚拟机将发起一次 Minor GC。

6. Minor GC 与 Major GC/Full GC的比较:

Minor GC:回收新生代(包括 Eden 和 Survivor 区域),因为 Java 对象大多都具备朝生夕灭的特性,所以 Minor GC 非常频繁,一般回收速度也比较快。

Major GC / Full GC:回收老年代,出现了 Major GC,经常会伴随至少一次的 Minor GC,但这并非绝对。Major GC 的速度一般会比 Minor GC 慢 10 倍 以上

在 JVM 规范中,Major GC 和 Full GC 都没有一个正式的定义,所以有人也简单地认为 Major GC 清理老年代,而 Full GC 清理整个内存堆。

7. 什么对象进入老年代

7.1. 大对象直接进入老年代

大对象是指需要大量连续内存空间的 Java 对象,如很长的字符串或数据。
一个大对象能够存入 Eden 区的概率比较小,发生分配担保的概率比较大,而分配担保需要涉及大量的复制,就会造成效率低下。
虚拟机提供了一个 -XX:PretenureSizeThreshold 参数,令大于这个设置值的对象直接在老年代分配,这样做的目的是避免在 Eden 区及两个 Survivor 区之间发生大量的内存复制。

7.2. 长期存活的对象将进入老年代

VM 给每个对象定义了一个对象年龄计数器。当新生代发生一次 Minor GC 后,存活下来的对象年龄 +1,当年龄超过一定值时,就将超过该值的所有对象转移到老年代中去。
使用 -XXMaxTenuringThreshold 设置新生代的最大年龄,只要超过该参数的新生代对象都会被转移到老年代中去。

8. 动态对象年龄判定

如果当前新生代的 Survivor 中,相同年龄所有对象大小的总和大于 Survivor 空间的一半,年龄 >= 该年龄的对象就可以直接进入老年代,无须等到 MaxTenuringThreshold 中要求的年龄。

9. 空间分配担保

jdk 1.6以后规则如下:
只要老年代的连续空间大于新生代对象总大小或者历次晋升的平均大小,就会进行 Minor GC,否则将进行 Full GC。
通过清除老年代中的废弃数据来扩大老年代空闲空间,以便给新生代作担保。
这个过程就是分配担保。

10. 那些情况会触发Full GC

(1).System.gc() 方法的调用 此方法的调用是建议 JVM 进行 Full GC,注意这只是建议而非一定,但在很多情况下它会触发 Full GC,从而增加 Full GC 的频率。通常情况下我们只需要让虚拟机自己去管理内存即可,我们可以通过 -XX:+ DisableExplicitGC 来禁止调用 System.gc()。
(2)老年代空间不足 老年代空间不足会触发 Full GC 操作,若进行该操作后空间依然不足,则会抛出如下错误:java.lang.OutOfMemoryError: Java heap space
(3)永久代空间不足 JVM 规范中运行时数据区域中的方法区,在 HotSpot 虚拟机中也称为永久代(Permanet Generation),存放一些类信息、常量、静态变量等数据,当系统要加载的类、反射的类和调用的方法较多时,永久代可能会被占满,会触发 Full GC。如果经过 Full GC 仍然回收不了,那么 JVM 会抛出如下错误信息:java.lang.OutOfMemoryError: PermGen space
(4)CMS GC 时出现 promotion failed 和 concurrent mode failure promotion failed,就是上文所说的担保失败,而 concurrent mode failure 是在执行 CMS GC 的过程中同时有对象要放入老年代,而此时老年代空间不足造成的。
(5) 统计得到的 Minor GC 晋升到旧生代的平均大小大于老年代的剩余空间。

11. 在高性能硬件上部署程序,目前主要有两种方式:

(1)通过 64 位 JDK 来使用大内存;
(2)使用若干个 32 位虚拟机建立逻辑集群来利用硬件资源。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/768106
推荐阅读
相关标签
  

闽ICP备14008679号