目录
性能优化及总结
JVM图解
GC优化
垃圾收集发生的时机
实验
GC日志文件分析工具
调优最佳指南G1(借鉴)
高并发场景分析
JVM性能优化指南
常见的问题及解答
内存泄露和内存溢出的区别
youngGC会有stw吗
major gc和full gc的区别
G1与CMS的区别是什么
什么是直接内存
垃圾判断的方式
不可达的对象一定要被回收吗?
f方法区中的无用类回收
不同的引用
为什么要区分新生代和老年代?
性能优化及总结
JVM图解
执行引擎:用于执行JVM字节码指令
主要有两种实现方式
(1)将输入的字节码指令在加载时或执行时翻译成另外一种虚拟机指令(解释执行)
(2)将输入的字节码指令在加载或执行时翻译成宿主主机本地CPU指令集(即时编译)
GC优化
内存被使用之后,难免会有不够用或达到设定值的时候,此时就需要对内存空间进行垃圾回收
垃圾收集发生的时机
GC是有JVM自动完成的,根据JVM的系统环境而定,所以时机是不确定的,当然我们也可以手动进行垃圾回收,比如调用System.gc()只是通知要回收,什么时候回收由JVM决定
一般以下几种情况会发生垃圾回收
- 当Eden区或者s区不够用了
- 老年代空间不够用了
- 方法区空间不够用了
- System.gc();
实验
1.得到GC日志文件 博主用的是eclipse 在eclipse根目录下找到eclipse.ini 加上
-verbose:gc
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintGCDateStamps
-Xloggc:gc.log
再启动Tomcat后会在eclipse根目录下产生GC日志
理解日志格式 https://blogs.oracle.com/poonam/understanding-g1-gc-logs
GC日志文件分析工具
https://gceasy.io
调优最佳指南G1(借鉴)
(1)
不要手动设置新生代和老年代的大小,只要设置整个堆的大小
G1
收集器在运行过程中,会自己调整新生代和老年代的大小
其实是通过
adapt
代的大小来调整对象晋升的速度和年龄,从而达到为收集器设置的暂停时间目标
如果手动设置了大小就意味着放弃了
G1
的自动调优
(2)
不断调优暂停时间目标
一般情况下这个值设置到
100ms
或者
200ms
都是可以的
(
不同情况下会不一样
)
,但如果设置成
50ms
就
不太合理。暂停时间设置的太短,就会导致出现
G1
跟不上垃圾产生的速度。最终退化成
FullGC
。所以
对这个参数的调优是一个持续的过程,逐步调整到最佳状态。暂停时间只是一个目标,并不能总是得到
满足。
(3)
使用
-XX:ConcGCThreads=n
来增加标记线程的数量
IHOP
如果阀值设置过高,可能会遇到转移失败的风险,比如对象进行转移时空间不足。如果阀值设置过
低,就会使标记周期运行过于频繁,并且有可能混合收集期回收不到空间。
IHOP
值如果设置合理,但是在并发周期时间过长时,可以尝试增加并发线程数,调高
ConcGCThreads
。
(4)MixedGC
调优
-XX:InitiatingHeapOccupancyPercent
-XX:G1MixedGCLiveThresholdPercent
-XX:G1MixedGCCountTarger
-XX:G1OldCSetRegionThresholdPercent
(5)
适当增加堆内存大小
高并发场景分析
JVM性能优化指南
常见的问题及解答
内存泄露和内存溢出的区别
内存泄漏是指不再使用的对象无法得到及时的回收,持续占用内存空间,从而造成内存空间的浪费。
内存泄漏很容易导致内存溢出,但内存溢出不一定是内存泄漏导致的。
youngGC会有stw吗
不管什么
GC
,都会发送
stop-the-world
,区别是发生的时间长短。而这个时间跟垃圾收集器又有关
系,
Serial
、
PartNew
、
Parallel Scavenge
收集器无论是串行还是并行,都会挂起用户线程,而
CMS
和
G1
在并发标记时,是不会挂起用户线程的,但其它时候一样会挂起用户线程,
stop the world
的时
间相对来说就小很多了。
major gc和full gc的区别
Major Gc
在很多参考资料中是等价于
Full GC
的,我们也可以发现很多性能监测工具中只有
Minor GC
和
Full GC
。一般情况下,一次
Full GC
将会对年轻代、老年代、元空间以及堆外内存进行垃圾回收。触
发
Full GC
的原因有很多:当年轻代晋升到老年代的对象大小,并比目前老年代剩余的空间大小还要大
时,会触发
Full GC
;当老年代的空间使用率超过某阈值时,会触发
Full GC
;当元空间不足时(
JDK1.7
永久代不足),也会触发
Full GC
;当调用
System.gc()
也会安排一次
Full GC
。
G1与CMS的区别是什么
CMS
主要集中在老年代的回收,而
G1
集中在分代回收,包括了年轻代的
Young GC
以及老年代的
Mix
GC
;
G1
使用了
Region
方式对堆内存进行了划分,且基于标记整理算法实现,整体减少了垃圾碎片的
产生;在初始化标记阶段,搜索可达对象使用到的
Card Table
,其实现方式不一样。
什么是直接内存
Java
的
NIO
库允许
Java
程序使用直接内存。直接内存是在
java
堆外的、直接向系统申请的内存空间。通
常访问直接内存的速度会优于
Java
堆。因此出于性能的考虑,读写频繁的场合可能会考虑使用直接内
存。由于直接内存在
java
堆外,因此它的大小不会直接受限于
Xmx
指定的最大堆大小,但是系统内存是
有限的,
Java
堆和直接内存的总和依然受限于操作系统能给出的最大内存。
垃圾判断的方式
引用计数法:指的是如果某个地方引用了这个对象就
+1
,如果失效了就
-1
,当为
0
就会回收但是
JVM
没
有用这种方式,因为无法判定相互循环引用(
A
引用
B,B
引用
A
)的情况
引用链法:通过一种
GC ROOT
的对象(方法区中静态变量引用的对象等
-static
变量)来判断,如果有
一条链能够到达
GC ROOT
就说明,不能到达
GC ROOT
就说明可以回收
不可达的对象一定要被回收吗?
即使在可达性分析法中不可达的对象,也并非是
“
非死不可
”
的,这时候它们暂时处于
“
缓刑阶段
”
,要真
正宣告一个对象死亡,至少要经历两次标记过程;可达性分析法中不可达的对象被第一次标记并且进行
一次筛选,筛选的条件是此对象是否有必要执行
finalize
方法。当对象没有覆盖
finalize
方法,或
finalize
方法已经被虚拟机调用过时,虚拟机将这两种情况视为没有必要执行。
被判定为需要执行的对象将会被放在一个队列中进行第二次标记,除非这个对象与引用链上的任何一个
对象建立关联,否则就会被真的回收。
f方法区中的无用类回收
方法区主要回收的是无用的类,那么如何判断一个类是无用的类的呢?
判定一个常量是否是
“
废弃常量
”
比较简单,而要判定一个类是否是
“
无用的类
”
的条件则相对苛刻许多。
类需要同时满足下面
3
个条件才能算是
“
无用的类
”
:
- 该类所有的实例都已经被回收,也就是 Java 堆中不存在该类的任何实例。
- 加载该类的 ClassLoader 已经被回收。
-
该类对应的
java.lang.Class
对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方
法。
虚拟机可以对满足上述
3
个条件的无用类进行回收,这里说的仅仅是
“
可以
”
,而并不是和对象一样不使
用了就会必然被回收。
不同的引用
JDK1.2以后,Java对引用进行了扩充:强引用、软引用、弱引用和虚引用
为什么要区分新生代和老年代?
当前虚拟机的垃圾收集都采用分代收集算法,这种算法没有什么新的思想,只是根据对象存活周期的不
同将内存分为几块。一般将
java
堆分为新生代和老年代,这样我们就可以根据各个年代的特点选择合
适的垃圾收集算法。
比如在新生代中,每次收集都会有大量对象死去,所以可以选择复制算法,只需要付出少量对象的复制
成本就可以完成每次垃圾收集。而老年代的对象存活几率是比较高的,而且没有额外的空间对它进行分
配担保,所以我们必须选择
“
标记
-
清除
”
或
“
标记
-
整理
”
算法进行垃圾收集。