1.垃圾收集器
相关概念见:深入理解jvm--垃圾收集器与内存分配策略(二)
垃圾收集器的开关
参数 |
新生代使用的GC |
老年代使用的GC |
-XX:+UseSerialGC |
serialGC |
serialOldGC |
-XX:+UseParNewGC |
ParNewGC |
serialOldGC |
-XX:+UseParallelGC |
ParallelGC |
serialOldGC |
-XX:+UseParallelOldGC |
ParallelGC |
ParallelOldGC |
-XX:+UseConcMarkSweepGC |
ParNewGC |
CMS |
-XX:+UseG1GC |
G1 |
G1 |
CMS收集器的相关参数:
-XX:ConcGCThreads/-XX:ParallelCMSThreads |
设置并发线程数量 |
-XX:CMSInitiatingOccupancyFraction |
设置回收阈值,默认空间使用率为68%时进行回收。 |
-XX:+UseCMSCompactAtFullCollection |
进行碎片整理 |
-XX:CMSFullGCsBeforeCompaction |
进行制定次数的CMS回收后进行内存压缩 |
-XX:+CMSClassUnloadingEnabled |
回收Perm区的class数据 |
G1收集器相关的参数:
-XX:MaxGCPauseMillis |
制定目标的最大停顿时间 |
-XX:ParallelGCThreads |
设置工作线程的数量 |
-XX:InitiatingHeapOccupancyPercent |
达到指定的堆使用率时进行并发标记周期的执行 |
示例1--垃圾回收日志的阅读
package chapter5;
public class GCtools {
private final static int _1MB = 1024 * 1024;
public static void allocation() {
byte[] allocation1, allocation2, allocation3, allocation4, allocation5;
allocation1 = new byte[6 * _1MB];
allocation2 = new byte[6 * _1MB];
allocation3 = new byte[1 * _1MB];
allocation3 = null;
allocation4 = new byte[1 * _1MB];
allocation4 = null;
allocation5 = new byte[1 * _1MB];
}
public static void main(String[] args) {
allocation();
}
}
输出结果列表
-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:+UseG1GC -Xloggc:gc.log -XX:+PrintGCDetails -XX:+PrintGCTimeStamps |
|
-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:+UseConcMarkSweepGC -Xloggc:gc.log -XX:+PrintGCDetails -XX:+PrintGCTimeStamps |
|
2.内存分配和回收的细节问题
1.禁用System.gc()
使用参数-XX:+DisableExplicitGC可禁止手工触发GC。
2.System.gc()使用并发回收
显示GC默认使用串行方式进行回收,使用“+ExplicitGCInvokesConcurrent”会采用并发方式进行回收。
3.并行GC前额外触发新生代的GC
示例2--并发收集器FullGC前的新生代GC
package chapter5;
public class ScavengeBeforeFullGC {
public static void main(String[] args) {
System.gc();
}
}
使用串行收集器并打印GC日志:
[Full GC[Tenured: 0K->464K(86400K), 0.0038260 secs] 1382K->464K(125248K), [Perm : 2562K->2562K(21248K)], 0.0038951 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
使用并行收集器并打印GC日志:(-XX:+PrintGCDetails -XX:+UseParallelGC)
[GC [PSYoungGen: 1331K->600K(38400K)] 1331K->600K(124416K), 0.0015175 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC [PSYoungGen: 600K->0K(38400K)] [ParOldGen: 0K->464K(86016K)] 600K->464K(124416K) [PSPermGen: 2562K->2561K(21504K)], 0.0109997 secs] [Times: user=0.05 sys=0.00, real=0.01 secs]
禁用新生代GC,(-XX:+PrintGCDetails -XX:+UseParallelGC -XX:-ScavengeBeforeFullGC)
[Full GC [PSYoungGen: 1331K->0K(38400K)] [ParOldGen: 0K->464K(86016K)] 1331K->464K(124416K) [PSPermGen: 2562K->2561K(21504K)], 0.0106025 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
可见,显示GC实际上出发了两次GC。尽可能地缩短一次GC所用的时间。
4.对象何时进入老年代
示例3--初创的对象在eden区
package chapter5;
//jvm参数:-Xmx64M -Xms64M -XX:+PrintGCDetails
public class AllocEden {
public static final int _1k = 1024;
public static void main(String[] args) {
for (int i = 0; i < 5*_1k; i++) {
byte[] b= new byte[_1k];
}
}
}
输出结果:
Heap
PSYoungGen total 19456K, used 6774K [0x00000000fea80000, 0x0000000100000000, 0x0000000100000000)
eden space 16896K, 40% used [0x00000000fea80000,0x00000000ff11dbd0,0x00000000ffb00000)
from space 2560K, 0% used [0x00000000ffd80000,0x00000000ffd80000,0x0000000100000000)
to space 2560K, 0% used [0x00000000ffb00000,0x00000000ffb00000,0x00000000ffd80000)
ParOldGen total 44032K, used 0K [0x00000000fbf80000, 0x00000000fea80000, 0x00000000fea80000)
object space 44032K, 0% used [0x00000000fbf80000,0x00000000fbf80000,0x00000000fea80000)
PSPermGen total 21504K, used 2569K [0x00000000f6d80000, 0x00000000f8280000, 0x00000000fbf80000)
object space 21504K, 11% used [0x00000000f6d80000,0x00000000f7002650,0x00000000f8280000)
示例4--老年对象进入老年代
package chapter5;
import java.util.HashMap;
import java.util.Map;
public class MaxTenuringThreshold {
public static final int _1M = 1024*1024;
public static final int _1k = 1024;
public static void main(String[] args) {
Map<Integer, byte[]> map = new HashMap<Integer, byte[]>();
for(int i=0;i<5*_1k;i++){
byte[] b = new byte[_1k];
map.put(i, b);
}
for (int k = 0; k < 17; k++) {
for(int i=0;i<270;i++){
byte[] g = new byte[_1M];
}
}
}
}
输出结果表:
-Xmx1024M -Xms1024M -XX:+PrintGCDetails -XX:+UseSerialGC -XX:MaxTenuringThreshold=15 -XX:+PrintHeapAtGC |
可以看到对象的年龄按照GC的次数逐渐增加,当达到设置的最大年龄时进入老年代 |
-Xmx1024M -Xms1024M -XX:+PrintGCDetails -XX:MaxTenuringThreshold=15 -XX:+UseSerialGC -XX:+PrintHeapAtGC -XX:TargetSurvivorRatio=15 |
可见,当from区达到目标使用率时,对象会提前进入老年代 |
示例4--大对象直接进入老年代
package chapter5;
import java.util.HashMap;
import java.util.Map;
public class PreTenureSizeThreshold {
public static final int _1K = 1024;
public static void main(String[] args) {
Map<Integer, byte[]> map = new HashMap<Integer, byte[]>();
for(int i=0;i<5*_1K;i++){
byte[] b = new byte[_1K];
map.put(i, b);
}
}
}
输出结果表:
-Xmx32M -Xms32M -XX:+UseSerialGC -XX:+PrintGCDetails |
Heap def new generation total 9792K, used 6647K [0x00000000f8e00000, 0x00000000f98a0000, 0x00000000f98a0000) eden space 8704K, 76% used [0x00000000f8e00000, 0x00000000f947dfb8, 0x00000000f9680000) from space 1088K, 0% used [0x00000000f9680000, 0x00000000f9680000, 0x00000000f9790000) to space 1088K, 0% used [0x00000000f9790000, 0x00000000f9790000, 0x00000000f98a0000) tenured generation total 21888K, used 0K [0x00000000f98a0000, 0x00000000fae00000, 0x00000000fae00000) the space 21888K, 0% used [0x00000000f98a0000, 0x00000000f98a0000, 0x00000000f98a0200, 0x00000000fae00000) compacting perm gen total 21248K, used 2576K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000) the space 21248K, 12% used [0x00000000fae00000, 0x00000000fb084318, 0x00000000fb084400, 0x00000000fc2c0000) No shared spaces configured. 可以看到,所有对象都分配在新生代 |
-Xmx32M -Xms32M -XX:+UseSerialGC -XX:+PrintGCDetails -XX:PretenureSizeThreshold=1000 |
Heap def new generation total 9792K, used 6648K [0x00000000f8e00000, 0x00000000f98a0000, 0x00000000f98a0000) eden space 8704K, 76% used [0x00000000f8e00000, 0x00000000f947e1b0, 0x00000000f9680000) from space 1088K, 0% used [0x00000000f9680000, 0x00000000f9680000, 0x00000000f9790000) to space 1088K, 0% used [0x00000000f9790000, 0x00000000f9790000, 0x00000000f98a0000) tenured generation total 21888K, used 0K [0x00000000f98a0000, 0x00000000fae00000, 0x00000000fae00000) the space 21888K, 0% used [0x00000000f98a0000, 0x00000000f98a0000, 0x00000000f98a0200, 0x00000000fae00000) compacting perm gen total 21248K, used 2576K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000) the space 21248K, 12% used [0x00000000fae00000, 0x00000000fb084318, 0x00000000fb084400, 0x00000000fc2c0000) No shared spaces configured. 当设置对象晋升老年代的大小后,对象并没有进入老年代。 |
-Xmx32M -Xms32M -XX:+UseSerialGC -XX:+PrintGCDetails -XX:PretenureSizeThreshold=1000 -XX:-UseTLAB |
Heap def new generation total 9792K, used 704K [0x00000000f8e00000, 0x00000000f98a0000, 0x00000000f98a0000) eden space 8704K, 8% used [0x00000000f8e00000, 0x00000000f8eb0230, 0x00000000f9680000) from space 1088K, 0% used [0x00000000f9680000, 0x00000000f9680000, 0x00000000f9790000) to space 1088K, 0% used [0x00000000f9790000, 0x00000000f9790000, 0x00000000f98a0000) tenured generation total 21888K, used 5413K [0x00000000f98a0000, 0x00000000fae00000, 0x00000000fae00000) the space 21888K, 24% used [0x00000000f98a0000, 0x00000000f9de9590, 0x00000000f9de9600, 0x00000000fae00000) compacting perm gen total 21248K, used 2576K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000) the space 21248K, 12% used [0x00000000fae00000, 0x00000000fb084318, 0x00000000fb084400, 0x00000000fc2c0000) No shared spaces configured. 当禁用对象在TLAB上分配后,可以看到大对象进入了老年代。 |
示例5--TLAB上分配对象
package chapter5;
public class UseTLAB {
public static void alloc(){
byte[] b = new byte[2];
b[0] = 1;
}
public static void main(String[] args) {
long b = System.currentTimeMillis();
for(int i=0;i<10000000;i++){
alloc();
}
long e = System.currentTimeMillis();
System.out.println(e-b);
}
}
输出结果表:
-XX:+UseTLAB -Xcomp -XX:-BackgroundCompilation -XX:-DoEscapeAnalysis -server |
-XX:-UseTLAB -Xcomp -XX:-BackgroundCompilation -XX:-DoEscapeAnalysis -server |
由于TLAB空间不会太大,大对象直接分配在堆上,TLAB空间较小因此有两种选择,一种是废弃当前的TLAB,另一种是直接分配到堆上。 |
-XX:+UseTLAB -XX:+PrintTLAB -XX:+PrintGC -XX:TLABSize=102400 -XX:-ResizeTLAB -XX:TLABRefillWasteFraction=100 -XX:-DoEscapeAnalysis -server |
TLAB: gc thread: 0x000000000b25e800 [id: 9912] desired_size: 100KB(TLAB的大小) slow allocs: 0(慢分配次数) refill waste: 1024B alloc: 0.15024 5000KB refills: 1 waste 99.7% gc: 102080B slow: 0B fast: 0B TLAB: gc thread: 0x0000000002e90800 [id: 104] desired_size: 100KB slow allocs: 1 refill waste: 1024B alloc: 0.15024 5000KB refills: 332 waste 0.0% gc: 0B slow: 5920B fast: 0B TLAB totals: thrds: 2 refills: 333 max: 332 slow allocs: 1 max 1 waste: 0.3% gc: 102080B max: 102080B slow: 5920B max: 5920B fast: 0B max: 0B [GC 33280K->568K(124416K), 0.0013079 secs] ... ... 296 当设置TLAB阈值后,大于TLAB空间*(1/阈值)的对象会直接在堆上分配 |
5.对象的分配流程
6.finalize方法对垃圾回收的影响
1.会导致对象复活。
2.执行时间没有保障,若不发生GC,则没有机会执行
3.耗时的finalize方法影响GC性能
示例5--finalize影响GC效率
package chapter5;
//-Xmx64M -Xms64M -XX:+PrintGCDetails
public class LongFinalize {
public static class LF{
private byte[] content = new byte[512];
@Override
protected void finalize() throws Throwable {
try{
System.out.println(Thread.currentThread().getId());
Thread.sleep(1000);
}catch(Exception e){
e.printStackTrace();
}
}
}
public static void main(String[] args) {
long b = System.currentTimeMillis();
for(int i=0;i<50000;i++){
LF f = new LF();
}
long e = System.currentTimeMillis();
System.out.println(e-b);
}
}
查看快照:
3.垃圾回收器对tomcat性能影响实验
1.配置JMeter
添加线程组
、
添加采样器,输入服务器地址
配置监听器
2.配置tomcat的jvm参数
有两种方式,一种是在Catalina.bat中直接添加,另一种是新建批处理程序setenv.bat进行添加:
CATALINA_OPTS用于控制tomcat本身的虚拟机参数,而JAVA_OPTS用于控制tomcat的全部进程使用。
参数不同时的报告:
set CATALINA_OPTS=-Xloggc:gc.log -XX:+PrintGCDetails -Xmx32M -Xms32M -XX:+HeapDumpOnOutOfMemoryError -XX:+UseSerialGC -XX:PermSize=32M |
set CATALINA_OPTS=-Xloggc:gc.log -XX:+PrintGCDetails -Xmx512M -XX:MaxPermSize=32M -XX:+HeapDumpOnOutOfMemoryError -XX:+UseSerialGC |
当最大堆扩大后,系统的吞吐量显著提升 |
set CATALINA_OPTS=-Xloggc:gc.log -XX:+PrintGCDetails -Xmx512M -Xms64M -XX:MaxPermSize=32M -XX:+UseSerialGC |
修改初始堆大小后,系统GC次数减少,吞吐量无明显变化 |
set CATALINA_OPTS=-Xloggc:gc.log -XX:+PrintGCDetails -Xmx512M -Xms64M -XX:MaxPermSize=32M -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:ParallelGCThreads=4 |
在使用并发垃圾回收器后,由于GC压力不大,吞吐量并无明显变化 |
set CATALINA_OPTS=-Xloggc:gc.log -XX:+PrintGCDetails -Xmx40M -Xms40M -XX:MaxPermSize=32M -XX:+UseSerialGC |
减小堆的大小后,GC压力变大,吞吐量增加。 |
set CATALINA_OPTS=-Xloggc:gc.log -XX:+PrintGCDetails -Xmx40M -Xms40M -XX:MaxPermSize=32M -XX:+UseParallelOldGC -XX:ParallelGCThreads=4 |
在有一定的GC压力后,使用并发收集器吞吐量有明显的增加 |
set CATALINA_OPTS=-Xloggc:gc.log -XX:+PrintGCDetails -Xmx40M -Xms40M -XX:MaxPermSize=32M -XX:+UseParNewGC |
使用全并行后比全串行的吞吐量增加 |