JVM
2023-12-04 17:51:47 0 举报
AI智能生成
java
作者其他创作
大纲/内容
调优
目的
减少GC的频率和Full GC的次数
原则
分析GC情况优化代码比优化GC参数要多得多
GC调优是最后的手段
选择合适的GC回收器
选择合适的堆大小
选择年轻代在堆中的比重
步骤
监控GC的状态
分析结果,判断是否需要优化
调整GC类型和内存分配
不断的分析和调整,寻找最优参数
全面应用参数
推荐策略
年轻代大小选择
1. 响应时间优先的应用:尽可能设大,直到接近系统的最低响应时间限制(根据实际情况选择).在此种情况下,年轻代收集发生的频率也是最小的.同时,减少到达年老代的对象.
2. 吞吐量优先的应用:尽可能的设置大,可能到达Gbit的程度.因为对响应时间没有要求,垃圾收集可以并行进行,一般适合8CPU以上的应用.
3. 避免设置过小.当新生代设置过小时会导致:1.YGC次数更加频繁 2.可能导致YGC对象直接进入旧生代,如果此时旧生代满了,会触发FGC.
年老代大小选择
1. 响应时间优先的应用:年老代使用并发收集器,所以其大小需要小心设置,一般要考虑并发会话率和会话持续时间等一些参数.如果堆设置小了,可以会造成内存碎片,高回收频率以及应用暂停而使用传统的标记清除方式;如果堆大了,则需要较长的收集时间.
2. 最优化的方案,一般需要参考以下数据获得:并发垃圾收集信息、持久代并发收集次数、传统GC信息、花在年轻代和年老代回收上的时间比例。
3. 吞吐量优先的应用都有一个很大的年轻代和一个较小的年老代.原因是,这样可以尽可能回收掉大部分短期对象,减少中期的对象,而年老代尽存放长期存活对象
实战
设置堆的最大最小值 -xms -xmx
调整老年和年轻代的比例
-XX:newSize设置绝对大小
防止年轻代堆收缩:老年代同理
主要看是否存在更多持久对象和临时对象
观察一段时间 看峰值老年代如何 不影响gc就加大年轻代
配置好的机器可以用 并发收集算法
每个线程默认会开启1M的堆栈 存放栈帧 调用参数 局部变量 太大了 500k够了-Xss 500k
方法区:将-XX:MetaspaceSize设置为较高的值,而-XX:MaxMetaspaceSize不进行设置
类加载
类的生命周期
加载
将.class文件从磁盘读到内存
连接
验证
验证字节码文件的正确性,包括: 1.文件格式验证; 2.元数据验证;3.字节码验证;4.符号引用验证
准备
在方法区中分配类变量所使用的内存空间
解析
虚拟机将常量池中的符号引用替换为直接引用13;【类装载器装入类所引用的其它所有类(静态链接)】
初始化
执行类构造器方法
使用
卸载
类加载器
启动类加载器(Bootstrap ClassLoader)
扩展类加载器(Extension ClassLoader)
应用程序类加载器(Application ClassLoader)
自定义类加载器
双亲委派
当一个类收到了类加载请求,他首先不会尝试自己去加载这个类,而是把这个请求委派给父类去完成,每一个层次类加载器都是如此,因此所有的加载请求都应该传送到启动类加载其中,只有当父类加载器反馈自己无法完成这个请求的时候(在它的加载路径下没有找到所需加载的Class), 子类加载器才会尝试自己去加载。
内存模型
线程私有
程序计数器
指向虚拟机字节码位置
唯一无OOM的区域
虚拟机栈
生命周期与线程相同,每个线程内调用每个方法创建一个栈帧
栈帧
局部变量表
存放方法参数和方法内部定义的局部变量
操作数栈
方法出口
动态链接
符号引用在运行期间转化为直接引用
异常
栈深度溢出:StackOverFlowError
栈拓展失败:OutOfMemoryError
本地方法栈13;(native method)
栈深度溢出:StackOverFlowError
栈拓展失败:OutOfMemoryError
线程共享
方法区
被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存
具体实现方式为:在本地内存中实现的元空间
回收:常量池的回收 (判断引用) 和 对类型的卸载
堆
新生代(1/3)
eden(8)
Java 新对象的出生地(如果新创建的对象占用内存很大,则直接分配到老年代)。当 Eden 区内存不够的时候就会触发MinorGC,对新生代区进行一次垃圾回收。
From Survivor(1)
上一次 GC 的幸存者,作为这一次 GC 的被扫描者。
To Survivor(1)
保留了一次 MinorGC 过程中的幸存者。
老年代(2/3)
主要存放应用程序中生命周期长的内存对象。
FULL GC 会产生STW问题
对象如何进入老年代?
如果对象够老,会通过“提升”进入老年代。
当 Survivor 空间不够,就需要依赖老年代进行分配担保
大对象直接在老年代分配
动态对象年龄判定
异常
OOM
垃圾回收
垃圾回收机制
判断对象存活
引用计数算法
强引用
软引用
弱引用
虚引用
可达性分析算法(GC Roots)
JVM中局部变量表中引用的对象
方法区中的静态引用、常量引用
JNI引用的对象
标记清除13;Mark-Sweep
分为两个阶段,标注和清除。标记阶段标记出所有需要回收的对象,清13;除阶段回收被标记的对象所占用的空间。此算法需要暂停整个应用,13;同时,会产生内存碎片。
缺点:效率不稳定,内存碎片化
标记复制13;Mark-Copying
按内存容量将内存划分为等大小的两块。每次只使用其中一块,当这一块内存满后将尚存活的对象复制到另一块上去,把已使用的内存清掉。
优点:实现简单,内存效率高,不易产生碎片。
缺点:可用内存被压缩到了原本的一半。且存活对象增多的话, Copying 算法的效率会大大降低。
标记整理13;Mark-Compact
标记阶段和 Mark-Sweep 算法相同, 标记后不是清理对象,而是将存活对象移向内存的一端。然后清除端边界外的对象。
分代收集
新生代:标记复制算法
老年代:标记清理/整理算法
垃圾收集器
回收类型
Minor GC:发生在年轻代的 GC
Major GC:发生在老年代的 GC
Full GC:全堆垃圾回收
CMS
年轻代使用复制算法,而对老年代使用标记-清除算法
回收过程
初始标记
只标记直接关联 GC root的对象,不用向下追溯,速度快
并发标记
标记 GC root所有可达的对象,和用户线程并行
重新标记
修正并发标记期间,因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,需要暂停所有的工作线程。
并发清理
清除GC Roots不可达对象,和用户线程并行
STW
优势
低延迟,尤其对于大堆来说。大部分垃圾回收过程并发执行。
劣势
内存碎片问题。Full GC 的整理阶段,会造成较长时间的停顿。
需要预留空间,用来分配收集阶段产生的“浮动垃圾”。
使用更多的 CPU 资源,在应用运行的同时进行堆扫描。
G1
G1(复制+标记清除)收集器是JDK9的默认垃圾收集器,而且不再区分年轻代和老年代进行回收。
G1 收集器避免全区域垃圾收集,它把堆内存划分为大小固定的几个独立区域,并且跟踪这些区域的垃圾收集进度,同时在后台维护一个优先级列表,每次根据所允许的收集时间,优先回收垃圾最多的区域。
回收过程
初始标记
标记了从GC Root开始直接可达的对象
并发标记
从GC Root开始对heap中的对象标记,标记线程与应用程序线程并行执行,并且收集各个Region的存活对象信息
最终标记
标记那些在并发标记阶段发生变化的对象,将被回收
筛选回收
清除空Region(没有存活对象的),加入到free list
0 条评论
下一页