理解GC
了解GC
每个搞java的都可能碰到OOME问题,通常的建议包括:
1 首先尝试增加MaxPermSize大小
2 增加最大堆内存-Xmx
本文将用Hotspot作为我们的JVM,介绍
GC,minor & Full GC, OOME以及JVM调优选项。
1 GC
GC负责:分配内存,确保引用对象保留在内存里,回收那些不再被引用的不可达对象的内存。
这个查找并移除可回收对象的过程可能会使应用程序暂停。
由Hotspot VM提供的GC是一个分代GC——内存被划分为几个部分,也就是说每一部分容纳不同生命周期的对象。这种设计是基于弱分代假说理论(Weak Generational Hypothesis):
1 大部分新生成的对象将很快死去
2 很少有从Old到Young的对象引用。
这种分代方式在很多应用程序中广泛应用,且在减少垃圾回收暂停时间和总成本方面已被证明是非常有效的。
Hotspot VM将堆空间划分为三个独立的空间:
1 Young代
(1)当app创建对象的时候,这些对象首先会被分配到Young代空间中。
(2)空间小且回收频繁
(3)所选Young代的GC算法通常速度比较快,因为Young回收频率高。
2 Old代
(1)存活时间长的对象最终“升级”到Old代
(2)通常比Young的对象大,且增长速度比较缓慢
(3)Old代GC的算法通常注重空间效率,因为old代占据了大部分的堆空间,所以GC算法必须要运行良好且具有比较低的垃圾回收密度。
3 Permanent代
(1)保存VM和Java类的元数据以及interned字符串和类的静态变量
(2)注意Java8会把Permanent给移除掉。
2 minor & Full GC
这三个空间中的某个满了并且需要额外的空间但没满足要求这时会发生GC现象。有两种GC:minor和full。当Young满了就会触发一个minor GC,surviving对象会被迁移到old。当old满了会触发full GC这个动作会涉及到整个对象堆。
Minor GC:
(1)当Young没有足够空间时会发生MinorGC。
(2)相对full GC往往是短暂的
Full GC:
(1)当Old或者Perm满了时,full gc就会发生。
(2)显式调用System.gc()时也可能会发生full GC。
(3)GC时间长短取决于堆大小,然而,如果GC时长超过3至5秒或更长的时间,那么我们认为它的GC时间就过长了。
其中Full GC的消耗时间最长,并且它是app不满足延时以及吞吐量需求的首要原因。简而言之GC调优的目标是减少full gc的数量跟频率。为了达到这个目标我们可以从两方面着手:
(1)从系统方面
a) 在不影响系统内存与磁盘换页的情况下,应该使用尽可能多的堆内存。建议JVM使用80%的可用RAM(除去其他app或者系统占有的)。
b) java堆越大,gc更好,app的吞吐量跟延迟表现会更佳。
(2)从app方面
减少对象的分配,更为重要的是减少对象在内存的驻留将有助于降低内存中存活数据的大小,从而有助于GC和应用程序性能的提升。
3 OOME
OOME是每个程序猿都不希望看到的,但它确实会发生,特别是当你的app涉及大量数据的处理时。App的整个内存包括:
(1)Java堆内存 (2)线程栈 (3)IO buffer (4)本地库分配的内存。
如果app用完内存或者JVM gc时回收更多空间失败,OOME异常就会抛出。注意OOME并不意味着内存泄漏。问题可能是由一个配置问题引发,例如指定的堆大小(未指定的话默认堆大小)不满足app需要。
4 JVM 调优选项
如果你的app堆比较小且gc花的时间比较长那么就该调整堆大小了。但我们不能把堆空间设置的太大,那样会影响系统其他程序的运行性能。
GC调优并不简单。找出优化空间大小会涉及到迭代的过程。假定我们已经成功定位到所要优化的堆空间大小,那么我们就可以用JVM命令项来设置这些值。最常用的有:
-Xms:设置初始和最小堆大小。例如-Xms512m
-Xmx: 设置java堆最大大小
-Xmn: 设置Young代大小。注意old代的大小将会基于Young代的值隐式设置。
-XX:PermSize=<n>[g|m|k]: 设置PermSize
-XX:MaxPermSize=<n>[g|m|k]:设置最大PermSize
每个搞java的都可能碰到OOME问题,通常的建议包括:
1 首先尝试增加MaxPermSize大小
2 增加最大堆内存-Xmx
本文将用Hotspot作为我们的JVM,介绍
GC,minor & Full GC, OOME以及JVM调优选项。
1 GC
GC负责:分配内存,确保引用对象保留在内存里,回收那些不再被引用的不可达对象的内存。
这个查找并移除可回收对象的过程可能会使应用程序暂停。
由Hotspot VM提供的GC是一个分代GC——内存被划分为几个部分,也就是说每一部分容纳不同生命周期的对象。这种设计是基于弱分代假说理论(Weak Generational Hypothesis):
1 大部分新生成的对象将很快死去
2 很少有从Old到Young的对象引用。
这种分代方式在很多应用程序中广泛应用,且在减少垃圾回收暂停时间和总成本方面已被证明是非常有效的。
Hotspot VM将堆空间划分为三个独立的空间:
1 Young代
(1)当app创建对象的时候,这些对象首先会被分配到Young代空间中。
(2)空间小且回收频繁
(3)所选Young代的GC算法通常速度比较快,因为Young回收频率高。
2 Old代
(1)存活时间长的对象最终“升级”到Old代
(2)通常比Young的对象大,且增长速度比较缓慢
(3)Old代GC的算法通常注重空间效率,因为old代占据了大部分的堆空间,所以GC算法必须要运行良好且具有比较低的垃圾回收密度。
3 Permanent代
(1)保存VM和Java类的元数据以及interned字符串和类的静态变量
(2)注意Java8会把Permanent给移除掉。
2 minor & Full GC
这三个空间中的某个满了并且需要额外的空间但没满足要求这时会发生GC现象。有两种GC:minor和full。当Young满了就会触发一个minor GC,surviving对象会被迁移到old。当old满了会触发full GC这个动作会涉及到整个对象堆。
Minor GC:
(1)当Young没有足够空间时会发生MinorGC。
(2)相对full GC往往是短暂的
Full GC:
(1)当Old或者Perm满了时,full gc就会发生。
(2)显式调用System.gc()时也可能会发生full GC。
(3)GC时间长短取决于堆大小,然而,如果GC时长超过3至5秒或更长的时间,那么我们认为它的GC时间就过长了。
其中Full GC的消耗时间最长,并且它是app不满足延时以及吞吐量需求的首要原因。简而言之GC调优的目标是减少full gc的数量跟频率。为了达到这个目标我们可以从两方面着手:
(1)从系统方面
a) 在不影响系统内存与磁盘换页的情况下,应该使用尽可能多的堆内存。建议JVM使用80%的可用RAM(除去其他app或者系统占有的)。
b) java堆越大,gc更好,app的吞吐量跟延迟表现会更佳。
(2)从app方面
减少对象的分配,更为重要的是减少对象在内存的驻留将有助于降低内存中存活数据的大小,从而有助于GC和应用程序性能的提升。
3 OOME
OOME是每个程序猿都不希望看到的,但它确实会发生,特别是当你的app涉及大量数据的处理时。App的整个内存包括:
(1)Java堆内存 (2)线程栈 (3)IO buffer (4)本地库分配的内存。
如果app用完内存或者JVM gc时回收更多空间失败,OOME异常就会抛出。注意OOME并不意味着内存泄漏。问题可能是由一个配置问题引发,例如指定的堆大小(未指定的话默认堆大小)不满足app需要。
4 JVM 调优选项
如果你的app堆比较小且gc花的时间比较长那么就该调整堆大小了。但我们不能把堆空间设置的太大,那样会影响系统其他程序的运行性能。
GC调优并不简单。找出优化空间大小会涉及到迭代的过程。假定我们已经成功定位到所要优化的堆空间大小,那么我们就可以用JVM命令项来设置这些值。最常用的有:
-Xms:设置初始和最小堆大小。例如-Xms512m
-Xmx: 设置java堆最大大小
-Xmn: 设置Young代大小。注意old代的大小将会基于Young代的值隐式设置。
-XX:PermSize=<n>[g|m|k]: 设置PermSize
-XX:MaxPermSize=<n>[g|m|k]:设置最大PermSize