JVM强化学习笔记

堆内分配与回收策略
对象优先在 Eden 区分配
大多数情况下,对象在新生代 Eden 区中分配,当 Eden 区没有足够空间进行分配时,虚拟机将发起
一次 Minor GC
什么是 Minor GC
指发生在新生代的垃圾收集,因为 Java 对象大多朝生夕灭,所以 Minor GC 非常频繁, 一般回收速 度也比较快
大对象直接进入老年代
-XX:PretenureSizeThreshold 当创建的对象超过指定大小时,直接把对象分配在老年代
大对象就是指需要大量连续内存空间的 Java 对象如字符串、数组,为了避免为大对象分配内存时
由于分配担保机制带来的复制而降低效率
长期存活的对象进入老年代
-XX:MaxTenuringThreshold 设定对象在 Survivor 区最大年龄阈值,超过阈值转移到老年代,默认
15 对象头的 Age 属性记录年龄,对象在 Eden 出生并经过第一次 Minor GC 后仍然能够存活,并且能 被 Survivor 容纳的话,将被移动到 Survivor 空间中,并将对象年龄设为 1 ,对象在 Survivor 中每熬过一次 MinorGC,年龄
就增加 1 岁,当它的年龄增加到一定程度(默认为 15 岁),就会被晋升到老年代中
动态对象年龄判定
并不一定要 Age 到达阈值才晋升到老年代,如果在 Survivor 空间中相同年龄所有对象大小的总和大
Survivor 空间的一半, 年龄大于或等于该年龄的对象就可以直接进入老年代 空间分配担保
-XX:HandlePromotionFailure
老年代的连续空间大于新生代对象总大小或者历次晋升到老年代对象的平均大小,就会进行 Minor
GC ,否则将进行 Full GC
GC 的分类
部分收集 (Partial GC)
1. 新生代收集( Minor GC / Young GC ):只对新生代进行垃圾收集
2. 老年代收集( Major GC / Old GC ):只对老年代进行垃圾收集。需要注意的是 Major GC
有的语境中也用于指代整堆收集;目前只有 CMS 收集器会有单独收集老年代的行为
3. 混合收集( Mixed GC ):对整个新生代和部分老年代进行垃圾收集 目前只有 G1
整堆收集 (Full GC) 收集整个 Java 堆 和 方法区
如何判定对象是否死亡
引用计数法:有地方引用它,计数器加一,引用失效,计数器减一 简单高效但是不能解决循环引
用问题可达性分析算法:通过一系列的称为 GC Roots 的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连的话对象不可达,则此对象不会再被使用
CMS 用增量更新 , G1 用原始快照来进行可达性分析,因为要保证在一致性的快照上进行对象图的遍历
可达性分析算法的优化
可达性分析算法中,从 GC Roots 集合找引用链时,为了避免从所有 GCRoots 候选位置中进行根节
点枚举,使用 OopMap 数据结构来直接得到什么地方存放着对象引用,并不需要真正一个不漏地
从方法区等 GC Roots 开始查找,缩短了根节点枚举时间
OopMap 存储哪两种对象引用
对象内的引用 在类加载完的时候, HotSpot 就把对象内什么偏移量上是什么类型的数据计算
出来 栈、寄存器中引用 在JIT 编译过程中,也会在特定的位置记录下栈和寄存器中哪些位置是引用
OopMap 生成条件
只会在安全点生成
什么是安全点
可达性分析算法必须是在一个确保一致性的内存快照中进行。如果在分析的过程中对象引用关系还
在不断变化,分析结果的准确性就不能保证。安全点意味着在这个点时,所有工作线程的状态是确
定的, JVM 就可以安全地执行 GC
安全点选取的条件 能让程序长时间执行的地方,这些指令代表着我们的代码将长时间不会继续往下执行,避免 GC 等待太久,就应该在这些地方设立安全点长时间执行 的最明显特征就是指令序列的复用, 例如方法调用、 循环跳转、 异常跳转等都属于指令序列复用
如何将线程停止在安全点
抢先式中断 系统中断所有用户线程,没到安全点的,恢复它,让其继续运行到安全点
主动式中断 在安全点设置一个标志,不断轮询这个中断标志,标志为真,就停在安全点指令
安全点存在的 因线程阻塞或休眠,无法响应虚拟机的中断请求,走到安全的地方去中断挂起自己的问题
什么是安全区
安全点的拓展,是指能够确保在某一段代码片段之中,引用关系不会发生变化,这个区域内,任何
地方垃圾回收都是安全的,解决了上述问题
可达性分析判定为不可达对象一定被回收吗
不一定,如果对象在可达性分析中没有与 GC Root 的引用链,那么此时就会被第一次标记并且进
行一次筛选,筛选的条件是是否有必要执行 finalize() 方法,当对象没有覆盖 finalize() 方法或者已
被虚拟机调用过,那么就认为是没必要的,如果该对象有必要执行 finalize() 方法,那么这个对象
将会放在一个称为 F - Queue 的队列中,虚拟机会触发一个 Finalizer 线程去执行它们的 finalize()
方法 ,此线程是低优先级的,并且虚拟机不会承诺一直等待它运行完,这是因为如果 finalize()
行缓慢或者发生了死锁,那么就会造成 F-Queue 队列一直等待,造成了内存回收系统的崩溃。之
GC 对处于 F-Queue 中的对象进行第二次被标记,这时,如果对象已经在在 finalize() 中成功拯
救自己 —— 只要重新与引用链上的任何一个对象建立关联即可,如把自己 (this 关键字 ) 赋值给某
个类变量或者对象的成员变量,那在第二次标记时它将被移出 " 即将回收 " 的集合,如果没有被移
出集合,就会被回收
可达性分析
通过三色标记算法解释 为什么要在一致性的快照上进行对象图的遍历
白色: 对象还未访问,最后还是白色说明对象不可达
黑色 : 可达对象 没有引用没扫描过
灰色: 已经被扫描过了但还有引用没扫描过
用户线程与收集线程并行,导致产生浮动垃圾,或则错误回收存活对象 ( 致命 )
1. 插入黑色对象到白色对象引用
2. 删除所有灰色对象到白色对象的直接或间接引用
增量更新用来解决错误回收存活对象
1. 插入的记下来,黑色变灰色 增量更新
2. 删除的也会按没删除搜索 原始快照
什么是 GC Roots GC Roots 特指的是垃圾收集器 (Garbage Collector) 的对象, GC 会收集那些不是 GC Roots 且没有
GC Roots 引用的对象
GC Roots 的对象包括哪几种
虚拟机栈 ( 栈帧中的局部变量表 ) 中引用的对象、本地方法栈 (Native 方法 ) 中引用的对象、方法区中
类静态属性引用的对象、方法区中常量引用的对象 、所有被同步锁持有的对象
四种引用类型
强引用:程序代码中的引用赋值,会持续存在,不会被回收,就算 OOM
软引用:软引用是用来描述一些还有用但并非必须的对象,如果内存空间足够,垃圾回收器就不会
回收它,在系统将要发生内存溢出之前,将会把这些对象列入回收范围之中进行第二次回收。如果
这次回收后还没有足够的内存,才会抛出内存溢出异常,用来实现内存敏感的高速缓存; JDK1.2
SoftReference
弱引用:弱引用也是用来描述非必须对象的,他的强度比软引用更弱一些,在垃圾回收时会直接回
收; JDK1.2 WeakReference
虚引用:一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通
知, JVM 只管理堆内内存, JDK 中直接内存的回收就用到虚引用, JAVA 在申请一块直接内存之后, 会在堆内存分配一个对象保存这个堆外内存的引用,这个对象被垃圾收集器管理,一旦这个对象被回收,相应的用户线程会收到通知并对直接内存进行清理工作;JDK1.2 PhantomReference
回收方法区
回收常量
字符串常量池中存在字符串 "abc" ,如果当前没有任何 String 对象引用该字符串常量的话,就
说明常量 "abc" 就是废弃常量,如果这时发生内存回收的话而且有必要的话, "abc" 就会被系
统清理出常量池了
回收类型
该类所有实例都回收;
该类的内加载器也被回收了;
该类对应的 Class 对象没在任何地方被引用,无法在任何地方通过反射访问该类的方法
垃圾收集算法
分代收集理论
弱分代假说:大部分对象都是朝生夕死
强分代假说:熬过越多次收集也难回收
跨代引假说:占少数 ( 可以通过记忆集,将老年代分成若干块,存在跨代才加入 GCRoots )
记忆集与卡表
局部收集,可能存在跨代引用,记忆集用来记录从非收集区指向收集区存在跨代引用的数据结构,
避免了把整个非收集区(老年代)加入 GCRoots 中,卡表是记忆集的实现
卡表有几种记录精度 字长精度:精确到一个机器字长(就是处理器寻址位数,比如 32 位或 64 位)
对象精度:精确到一个对象
卡精度:精确到一块内存区域
HotSpot 中一个卡页是多大
一般来讲卡页大小是 2 N 次幂
卡表的工作方式
只要卡页内有一个或多个对象的字段存在着跨代指针,那就将对应卡表的数组元素的值标识为 1
称这个元素变脏,在垃圾收集时,只要筛选出卡表中变脏的元素就能得出哪些卡页内存块中包含跨
代指针,把他们加入 GC Roots 中一并扫描
卡表如何更新
通过写屏障
用来更新卡表, G1 之前全是写后屏障, 用于引用类型字段赋值时更新卡表。 G1 还需要写前屏障来实现SATB
ZGC 只需要读屏障 ( 得益于 1. 无分代, 2. 染色指针 )
垃圾收集算法
1. 标记 清除算法:先标记需清除的对象,之后统一回收。这种方法效率不高,会产生大量不连续的碎片,且对象越多,效率越低;
2. 标记 复制算法:最开始将可用内存按容量划分为大小相等的两块 1 1 ,每次只使用其中一块。 当使用的这块空间用完了,就将存活对象复制到另一块,再把已使用过的内存空间一次清理掉。优点是没有空间碎片,简单高效,缺点浪费大量空间;Appel 式 回收将新生代分成 Eden 8
Survivor 1 Eden 和 一个 Survivor 用于分配,另外 一 Survivor 用于复制,并使用老年代进行分
配担保 - XX:HandlePromotionFailure -XX:SurvivorRatio 8
3. 标记 整理算法:先标记存活对象,然后让所有存活对象向一端移动,之后清理端边界以外的内存;移动回收复杂(更新引用),不移动碎片化分配复杂,总体来说移动的吞吐量最高,CMS( 关注低延迟) 先用标记 - 清除,空间碎片过多,使用标记 整理 -
XX:CMSFullGCsBeforeCompaction CMS 收集器执行过若干次不整理空间的 Full GC 之后, 下一
次进入 Full GC 前会先进行碎片整理
垃圾收集器
Serial
单线程 客户端模式 C1
简单而高效, Serial 收集器由于没有线程交互的开销,可以获得很高的单线程收集效率
ParNew 多线程并行 其余行为(控制参数、收集算法、回收策略等等)和 Serial 收集器完全一样
服务端 模式C2
Parallel Scavenge
ParNew 相似 吞吐量可控,可以自己设定一个期望,选择 自适应调节策略
Serial Old
Serial 收集器的老年代版本,单线程,作为 CMS 并发收集失败的逃生门 或 JDK5 及之前搭配
Parallel Scavenge
Parallel Old
Parallel Scavenge 收集器的老年代版本,在注重吞吐量以及 CPU 资源的场合,都可以优先考虑
Parallel Scavenge 收集器和 Parallel Old 收集器 JDK1.8 默认使用该搭配 收集新生代和老年代都会 Stop the word 新生代标记复制,老年代标记整理 CMS 第一款并发收集器,它第一次实现了让垃圾收集线程与用户线程( 基本上 ) 同时工作基于标记 - 清除 运作过程的四个步骤
初始标记 STW 标记 GCRoots 能直接关联到的对象
并发标记 从 GC Roots 的直接关联对象开始遍历整个对象图
重新标记 修正并发标记期间因为用户程序继续运行而导致标记产生变动的那一部分对象的标记记
停顿时间比并发标记短,比初始标记长
并发清除 开启用户线程,同时 GC 线程开始对未标记的区域做清扫
CMS 缺点
CMS 对处理器非常敏感,处理核数量越少,对用户线程影响越大
CMS 无法处理浮动垃圾, CMS 和用户线程并发运行期间预留的内存不够新对象分配,导致并发失败
STW 使用 Serial Old 回收
它使用的回收算法 " 标记 - 清除 " 算法会产生大量空间碎片产生
浮动垃圾是怎么样产生
CMS 的并发标记和并发清理阶段, 用户线程是还在继续运行的, 程序在运行自然就还会伴随有新
的垃圾对象不断产生, 但这一部分垃圾对象是出现在标记过程结束以后,只能下次垃圾回收处理
G1
G1 JDK9 后 服务端模式下默认的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器,它是一
款能够建立起 " 停顿时间模型 " 的收集器 —— 当我们指定在一个长度为 M 毫秒的时间片段的时候,这个
垃圾收集器消耗在垃圾收集上的时间大概率不会超过 M 毫秒
怎样建立起可靠的停顿预测模型
Region 和 回收集
G1 仍是遵循分代收集理论, 但不再坚持固定大小以及固定数量的分代区域划分, 而是把堆划分为
多个大小相等的区域( Region ) 每个 1~32M ( 总是 2 的幂次方 ) ,每一个 Region 都可以根据需要,
扮演不同的角色,收集器能够对扮演不同角色的 Region 采用不同的策略去处理,以达到更好的效
果每次回收不针对所有Region ,将回收 Region 价值排序,根据停顿的期望值,选择 Region 加入回收集
四种不同 Region 标签
Eden Survivor Old Humongous H 区可以认为是 Old 区中一种特列专门用来存储大数据的
0.5 Region <= 当对象大小 <= 1 Region 时候将数据存储到 H 区 ;当对象大小 > 1 Region 存储
到连续的 H
G1 特点 1. 空间整合: G1 从整体上看是基于标记 - 整理算法实现的,从局部 ( 两个 Region 之间 ) 上看是基于复制算法实现的,G1 运行期间不会产生内存空间碎片
2. 可预测停顿: G1 CMS 牛在能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为 M 毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N 毫秒
3. 并行与并发: 能充分利用多 CPU 、多核环境下的硬件优势;可以并行来缩短 "Stop The World" 停顿时间,也可以 并发让垃圾收集与用户程序同时进行
4. 分代收集 : 分代概念在 G1 中依然得以保留,但 G1 不再坚持固定大小以及固定数量的分代区域划分,改用角色不同的大小相等的Region 来管理,对不同的 Region 采用不同的策略以达到更好的效果
Region 里面存在的跨 Region 引用对象如何解决
每个 Region 都有一个记忆集的实现 —— 双向卡表卡表记录了其他Region 中的对象引用本 Region 中对象的关系, ( 谁引用了我的对象),它还记录了我指向谁,双向卡表结构实现比CMS 实现复杂,且每个 Region 都有一份占用更多内存, CMS 只有一份
Hashtable <key int[]> key 是我指向谁 key = Region 1 —— Region 2 指向 Region 1 int[] 中元素 为1 ,代表 Region 1 中的第 2 块与 第 4 块区域存在对 Region 1 的引用
在并发标记阶段如何保证收集线程与用户线程互不干扰地运行
并发标记阶段,为了分配对象和垃圾回收并发执行,每个 Region 设计了两个 TAMS 的指针 ——
PrevTAMS NextTAMS ,在 NextTAMS 指针之后给新对象分配内存
G1 会把存活对象的标记存放在哪 SATB 具体是什么呢
G1 是借助 bitmap 来存放对象存活标记,每一个 bit 表示每个 Region 中的某个对象起始地址,如
bit 标记为 1 ,则表示该对象存活
SATB 初始标记开始时,G1 收集器打了一个快照,形成一个所谓的对象图,这个对象图记录在 next marking bitmap 之中
并发标记阶段会在这个 bitmap 中记录对象存活标记
最终标记阶段,完成对快照对象图所有标记
NextTAMS 指针之后的内容,在这一次的 GC 周期内并不关注清理阶段,next marking bitmap previous marking bitmap 会发生置换, next marking bitmap 在下一次周期开始前会被清空;那么此时这个 Region previous marking bitmap 可以 直接表示出 Region [PrevTAMS NextTAMS ,在指针位置之上给新对象分配内存 , NextTAMS) 这个区间内存活对象数量,并且可以根据 bitmap 算出存活对象的具体地址,辅助下一步的 Evacuation(选取 CSet ,拷贝并合并存活对象到新的 Region 里),回收的同时减少了内存碎片, 当然 Evacuation 也是 STW 的 至此完成了一次全局并发标记周期
G1 GC 模式是怎样的
YoungGC
STW RSet 作为根集扫描获取存活对象,将 Eden/Survivor Region 中的存活对象拷贝并合并到
一个新的 Region 里 以减少内存碎片
Mixed GC
1. 初始标记: 标记 GC ROOT 能关联到的对象,需要 STW
2. 并发标记: 从 GCRoots 的直接关联对象开始遍历整个对象图的过程,扫描完成后还会重新处理
SATB 记录的在并发标记过程中引用发生变动的对象
3. 最终标记: 短暂暂停用户线程,处理剩下的 SATB 记录,需要 STW
4. 筛选回收: 更新 Region 的统计数据,对每个 Region 的回收价值和成本排序,根据用户设置的停顿
时间制定回收计划。再把需要回收的 Region 中存活对象复制到空的 Region ,同时清理旧的
Region ,需要 STW
G1 CMS 的对比
可预测的停顿 这是 G1 相对于 CMS 的另一个大优势,降低停顿时间是 G1 CMS 共同的关注
点,但 G1 除了追求低停顿外,还能建立可预测的停顿时间模型
记忆集复杂,每个 Region 都有一份,内存占用高,某些极端情况下会达到堆空间的 20% 甚至更多
CMS 基于增量更新, G1 基于 SATB 原始快照
CMS G1 都用写后屏障更新卡表, G1 还需要写前屏障来实现 SATB
ZGC
ZGC 收集器是一款基于 Region 内存布局的,不设分代的, 使用了读屏障 、 染色指针和内存多重映射
等技术来实现可并发的 标记 整理算法的, 以低延迟为首要目标的一款垃圾收集器
ZGC Region
小型 Region :容量固定为 2MB ,用于放置小于 256KB 的对象
中型 Region :容量固定为 32MB ,用于放置大于等于 256KB 但小于 4MB 的对象
大型 Region :容量不固定,可以动态变化,但必须放置为 2MB 的整数倍,用于放置 4MB 或以上的
大对象。每个 Region 只放一个大对象,也就是说他的大小可以小于中型 Region 大型 Region
ZGC 实现中不会被重分配,因为复制一个大对象的代价很高昂
什么是染色指针
染色指针指的是把一些对象的信息标记在引用对象的指针上,这些信息包括了其引用的三色标记状态、
是否进入了重分配集,是否只能通过 finalize() 方法访问到,但是由于这些信息是被标识在寻址指针的高 4
位上( linux 64 位指针,只支持 46 位)所以,对于 ZGC 可管理的内存来说,最高只有 2 ^ 42 次方,也
就是 4TB
染色指针的优势
Region 上存活对象被移走之后,能立马重用该 Region ;而不必等待整个堆中所有指向该 Region
引用都被修正后才能清理 ( 自愈 )
使用染色指针可以只用读屏障, 提升了吞吐量
可拓展增强功能,目前还有很多位没用
染色指针如何寻址
多重映射 :将染色指针的标志位看作地址分段符,将不同分段映射到同一个物理空间 ( 多对一 ) ,经
过多重映射转换后就可以寻址了
映射就是操作系统的虚拟存储器管理的功能,将虚拟地址空间映射到物理地址空间
ZGC 运作过程
四个阶段都能并发
1. 并发标记:遍历对象图做可达性分析 前后也要经过类似于 G1 的初始标记、 最终标记的短暂停顿 ,
记阶段会更新染色指针中的 Marked 0 Marked 1 标志
2. 并发预备重分配:扫描所有 Region ,选出要收集的 Region ,组成重分配集 (RSet)
3. 并发重分配:将重分配集中存活的对象复制到新 Region 中,并为重分配集中每个 Region 维护一个
转发表,当用户线程访问重分配集合中的对象,被读屏障截获,通过转发表指向新对象,并修改引
—— 也叫指针自愈,当该 Region 存活的对象复制完毕,该 Region 就可以重新使用
4. 并发重映射:修改剩下的指向堆中重分配集合的旧引用,释放转发表
在正式 Minor GC 前, JVM 会先检查新生代中对象,是比老年代中剩余空间大还是小。假如 Minor GC
Survivor 区放不下剩余对象,这些对象就要进入老年代 老年代剩余空间大于新生代中的对象大小,那
就直接 Minor GC GC Survivor 不够放,老年代也绝对够放;老年代剩余空间小于新生代中的对象
大小,这个时候就要查看是否启用了老年代空间分配担保规则
触发 Full GC 的条件
1. 调用 System.gc() 建议 JVM 进行 Full GC
2. CMS 并发失败,使用 Serial Old 收集
3. 空间分配担保失败
4. 老年代空间不足
Java 堆溢出
Java 堆中没有内存完成实例分配,并且堆也无法再扩展时, Java 虚拟机将会抛出
OutOfMemoryError 异常
溢出原因
应用程序保存了无法被 GC 回收的对象
排查解决思路
查找关键字报错信息 java.lang.OutOfMemoryError: Java heap space
使用内存映像分析工具 Jprofiler ,对 Dump 出来的堆储存快照进行分析,分析清楚是内存泄漏还
是内存溢出
如果是内存泄漏,可进一步通过工具查看泄漏对象到 GC Roots 的引用链,修复应用程序中的内存
泄漏
如果不存在泄漏,先检查代码是否有死循环,递归等,再考虑用 -Xmx 增加堆大小
栈溢出
如果线程请求的栈深度大于虚拟机所允许的深度,将抛出 StackOverflowError 异常; 如果 Java 虚拟机
栈容量可以动态扩展,当栈扩展时无法申请到足够的内存会抛出 OutOfMemoryError 异常 溢出原因
在单个线程下,无论是由于栈帧太大还是虚拟机栈容量太小,当内存无法分配的时候,抛出
StackOverflowError 异常
不断地建立线程的方式会导致内存溢出
排查解决思路
查找关键报错信息,确定是 StackOverflowError 还是 OutOfMemoryError
如果是 StackOverflowError ,检查代码是否递归调用方法等
如果是 OutOfMemoryError ,检查是否有死循环创建线程等,通过 -Xss 降低的每个线程栈大小的
容量
方法区溢出
运行时产生大量的类,填满方法区,或者当常量池无法再申请到内存时会抛出 OutOfMemoryError 异常
溢出原因
使用 CGLib 生成了大量的代理类,导致方法区被撑爆
Java7 之前,频繁的错误使用 String.intern() 方法
排查解决思路
检查是否使用 CGLib 生成了大量的代理类
检查代码是否频繁错误得使用 String.intern() 方法
直接内存溢出
直接内存也被频繁地使用,也可能导致 OOM
溢出原因
本机直接内存的分配虽然不会受到 Java 堆大小的限制,但是受到本机总内存大小限制
NIO 程序中,使用 ByteBuffer.allocteDirect(capability) 分配的是直接内存,可能导致直接内存溢
直接内存由 -XX:MaxDirectMemorySize 指定,如果不指定,则默认与 Java 堆最大值( -Xmx 指定)
一样
排查解决思路
检查代码是否恰当
检查 JVM 参数 -Xmx -XX:MaxDirectMemorySize 是否合理
性能监控故障处理
CPU 100%
1. ps -ef | grep 运行的服务名字,直接 top 命令也可以看到各个进程 CPU 使用情况
2. top -Hp PID 显示进程 PID 下所有的线程,定位到消耗 CPU 最高的线程 top -H -p 特定进程中的线
3. 将线程 ID 转换成 16 进制 printf '%x\n' 4. jstack 导出进程当前时刻的线程快照到文件
5. 最后用 cat 命令结合 grep 命令对十六进制线程 PID 进行过滤,可以定位到出现问题的代码
监控和故障处理工具
jps 查看所有 JAVA 进程
jstat 监视虚拟机各种运行状态信息
jinfo 实时查看和修改虚拟机参数,不需要重启
jmap 生成堆转储快照 dump
jhat 分析 dump 文件
jstack 生成虚拟机当前时刻的线程快照
可视化工具
Jconsole
Visual VM 功能的集合,比 Jprofiler 强太多
调优篇
GC 调优
GC 调优目的: GC 时间够少 , GC 次数够少
minor GC 单次耗时 < 50ms ,频率 10 秒以上。说明年轻代 OK
Full GC 单次耗时 < 1 秒,频率 10 分钟以上,说明年老代 OK
1. -Xms 5m 设置 JVM 初始堆为 5M -Xmx 5m 设置 JVM 最大堆为 5M -Xms -Xmx 值一样时可以避免
每次垃圾回收完成后 JVM 重新分配内存
2. -Xmn 2g: 设置年轻代大小为 2G ,一般默认为整个堆区的 1/3 ~ 1/4 -Xss 每个线程栈空间设置
3. -XX:SurvivorRatio ,设置年轻代中 Eden 区与 Survivor 区的比值,默认 =8 ,比值为 8:1:1
4. -XX:+HeapDumpOnOutOfMemoryError** JVM 发生 OOM 时,自动生成 DUMP 文件
5. -XX:PretenureSizeThreshold 当创建的对象超过指定大小时,直接把对象分配在老年代。
6. -XX:MaxTenuringThreshold 设定对象在 Survivor 区最大年龄阈值,超过阈值转移到老年代,默认
15
7. 开启 GC 日志对性能影响很小且能帮助我们定位问题
-XX:+PrintGCTimeStamps - XX:+PrintGCDetails -Xloggc:gc.log 日志位置
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值