堆内分配与回收策略
对象优先在
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
日志位置