跟踪垃圾回收和类加载信息
1.跟踪垃圾回收
示例---读懂日志:
package chapter3;
/**
* 创建实例,演示垃圾回收,进行4次minorGC
*
* @author shiker
* 预设参数:-verbose:gc -Xms20M -Xmx20M(设置堆容量为20M) -Xmn10M(新生代容量10M) -XX:SurvivorRatio=8(survivor占比8:1)
* -XX:+UseSerialGC(串行收集)
*/
public class GC {
private final static int _1MB = 1024 * 1024;
public static void allocation() {
byte[] allocation1, allocation2, allocation3, allocation4, allocation5;
allocation1 = new byte[6 * _1MB];
allocation1 = null;
allocation2 = new byte[6 * _1MB];
allocation2 = null;
allocation3 = new byte[6 * _1MB];
allocation3 = null;
allocation4 = new byte[6 * _1MB];
allocation4 = null;
allocation5 = new byte[6 * _1MB];
}
public static void main(String[] args) {
allocation();
}
}
打印GC日志:-XX:+PrintGC |
|
打印详细GC信息:-XX:+PrintGCDetails |
|
打印详细堆信息:-XX:+PrintHeapAtGC |
|
输出GC发生时间:-XX:+PrintGCTimeStamps |
|
输出程序执行时间:-XX:+PrintGCApplicationConcurrentTime |
|
输出程序中断时间:-XX:+PrintGCApplicationStoppedTime |
|
打印程序日志:-Xloggc:log/gc.log |
|
2.类的加载/卸载跟踪
示例2---类的加载/卸载
package chapter3;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
//继承classloader
public class UnloadClass extends ClassLoader implements Opcodes{
public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
//定义一个叫做Example的类
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS|ClassWriter.COMPUTE_FRAMES);
cw.visit(Opcodes.V1_7,Opcodes.ACC_PUBLIC , "Example", null, "java/lang/Object", null);
//生成默认构造方法
MethodVisitor mw = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
//生成构造方法的字节码指令
mw.visitVarInsn(ALOAD, 0);
mw.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
mw.visitInsn(RETURN);
mw.visitMaxs(1, 1);
mw.visitEnd();
//生成main方法
mw = cw.visitMethod(ACC_PUBLIC+ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
//生成main方法中的字节码指令
mw.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mw.visitLdcInsn("hello world");
mw.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
mw.visitInsn(RETURN);
mw.visitMaxs(2, 2);
//字节码生成完成
mw.visitEnd();
byte[] code = cw.toByteArray();
for(int i=0;i<10;i++){
UnloadClass loader = new UnloadClass();
Method m = ClassLoader.class.getDeclaredMethod("defineClass", String.class,byte[].class,int.class,int.class);
m.setAccessible(true);
m.invoke(loader, "Example",code,0,code.length);
//Class<?> exampleClass = loader.defineClass("Example", code, 0, code.length);
m.setAccessible(false);
System.gc();
}
}
}
jvm参数设置:
-XX:+TraceClassUnloading(打印卸载日志) -XX:+TraceClassLoading(打印加载日志) |
输出结果:
...
[Loaded java.lang.SecurityException from D:\jre1.7\lib\rt.jar]
[Loaded java.lang.Void from D:\jre1.7\lib\rt.jar]
[Loaded org.objectweb.asm.ClassVisitor from file:/D:/program2015/jvm/src/org.objectweb.asm-3.2.0.jar]
[Loaded org.objectweb.asm.ClassWriter from file:/D:/program2015/jvm/src/org.objectweb.asm-3.2.0.jar]
[Loaded org.objectweb.asm.FieldVisitor from file:/D:/program2015/jvm/src/org.objectweb.asm-3.2.0.jar]
[Loaded org.objectweb.asm.AnnotationVisitor from file:/D:/program2015/jvm/src/org.objectweb.asm-3.2.0.jar]
[Loaded org.objectweb.asm.MethodVisitor from file:/D:/program2015/jvm/src/org.objectweb.asm-3.2.0.jar]
[Loaded org.objectweb.asm.ByteVector from file:/D:/program2015/jvm/src/org.objectweb.asm-3.2.0.jar]
[Loaded org.objectweb.asm.Item from file:/D:/program2015/jvm/src/org.objectweb.asm-3.2.0.jar]
[Loaded org.objectweb.asm.MethodWriter from file:/D:/program2015/jvm/src/org.objectweb.asm-3.2.0.jar]
[Loaded org.objectweb.asm.Type from file:/D:/program2015/jvm/src/org.objectweb.asm-3.2.0.jar]
[Loaded org.objectweb.asm.Label from file:/D:/program2015/jvm/src/org.objectweb.asm-3.2.0.jar]
[Loaded java.lang.IllegalStateException from D:\jre1.7\lib\rt.jar]
[Loaded org.objectweb.asm.Frame from file:/D:/program2015/jvm/src/org.objectweb.asm-3.2.0.jar]
[Loaded java.lang.ClassFormatError from D:\jre1.7\lib\rt.jar]
[Loaded java.io.IOException from D:\jre1.7\lib\rt.jar]
[Loaded java.lang.AssertionStatusDirectives from D:\jre1.7\lib\rt.jar]
[Loaded java.lang.Integer$IntegerCache from D:\jre1.7\lib\rt.jar]
[Loaded sun.reflect.NativeMethodAccessorImpl from D:\jre1.7\lib\rt.jar]
[Loaded sun.reflect.DelegatingMethodAccessorImpl from D:\jre1.7\lib\rt.jar]
[Loaded Example from __JVM_DefineClass__]
[Loaded Example from __JVM_DefineClass__]
[Unloading class Example]
[Loaded Example from __JVM_DefineClass__]
[Unloading class Example]
[Loaded Example from __JVM_DefineClass__]
[Unloading class Example]
[Loaded Example from __JVM_DefineClass__]
[Unloading class Example]
[Loaded Example from __JVM_DefineClass__]
[Unloading class Example]
[Loaded Example from __JVM_DefineClass__]
[Unloading class Example]
[Loaded Example from __JVM_DefineClass__]
[Unloading class Example]
[Loaded Example from __JVM_DefineClass__]
[Unloading class Example]
[Loaded Example from __JVM_DefineClass__]
[Unloading class Example]
[Loaded java.lang.Shutdown from D:\jre1.7\lib\rt.jar]
[Loaded java.lang.Shutdown$Lock from D:\jre1.7\lib\rt.jar]
示例3---打印类信息表
package chapter3;
public class Loop {
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 100; i++) {
System.out.println("hello world!");
Thread.sleep(10000);
}
}
}
通过输入-XX:+PrintClassHistogram在程序执行时按ctrl+break(=ctrl+fn+B)输出类信息表:
3.系统参数查看
示例4--查看系统参数
代码沿用示例3。
虚拟机收到的命令行的显示参数:-XX:+PrintVMOptions |
|
打印传递给虚拟机的显示参数和隐式参数:-XX:+PrintCommandLineFlags |
|
查看系统的详细参数:-XX:+PrintFlagsFinal |
|
2.学习堆的配置参数
1.最大堆和最小堆的设置
示例5---配置堆参数
package chapter3;
public class HeapAlloc {
private final static int _1MB = 1024*1024;
public static void main(String[] args) {
System.out.print("maxMemory=");
System.out.println(Runtime.getRuntime().maxMemory()/_1MB+"MB");
System.out.print("free mem=");
System.out.println(Runtime.getRuntime().freeMemory()/_1MB+"MB");
System.out.print("total meme");
System.out.println(Runtime.getRuntime().totalMemory()/_1MB+"MB");
byte[] b = new byte[1*_1MB];
System.out.println("分配1M空间给数组");
System.out.print("maxMemory=");
System.out.println(Runtime.getRuntime().maxMemory()/_1MB+"MB");
System.out.print("free mem=");
System.out.println(Runtime.getRuntime().freeMemory()/_1MB+"MB");
System.out.print("total meme");
System.out.println(Runtime.getRuntime().totalMemory()/_1MB+"MB");
b = new byte[4*_1MB];
System.out.println("分配4M空间给数组");
System.out.print("maxMemory=");
System.out.println(Runtime.getRuntime().maxMemory()/_1MB+"MB");
System.out.print("free mem=");
System.out.println(Runtime.getRuntime().freeMemory()/_1MB+"MB");
System.out.print("total meme");
System.out.println(Runtime.getRuntime().totalMemory()/_1MB+"MB");
}
}
jvm参数设置为:
-Xmx20M -Xms5M -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseSerialGC |
输出结果为:
-XX:InitialHeapSize=5242880 -XX:MaxHeapSize=20971520 -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseSerialGC
maxMemory=19MB
free mem=4MB
total meme4MB
[GC[DefNew: 658K->127K(1536K), 0.0017391 secs] 658K->466K(4992K), 0.0017965 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
分配1M空间给数组
maxMemory=19MB
free mem=3MB
total meme4MB
[GC[DefNew: 1180K->0K(1536K), 0.0015228 secs][Tenured: 1490K->1490K(3456K), 0.0031215 secs] 1518K->1490K(4992K), [Perm : 2563K->2563K(21248K)], 0.0046967 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
分配4M空间给数组
maxMemory=19MB
free mem=3MB
total meme9MB
Heap
def new generation total 1664K, used 77K [0x00000000f9a00000, 0x00000000f9bc0000, 0x00000000fa0a0000)
eden space 1536K, 5% used [0x00000000f9a00000, 0x00000000f9a135b0, 0x00000000f9b80000)
from space 128K, 0% used [0x00000000f9b80000, 0x00000000f9b80000, 0x00000000f9ba0000)
to space 128K, 0% used [0x00000000f9ba0000, 0x00000000f9ba0000, 0x00000000f9bc0000)
tenured generation total 7556K, used 5586K [0x00000000fa0a0000, 0x00000000fa801000, 0x00000000fae00000)
the space 7556K, 73% used [0x00000000fa0a0000, 0x00000000fa614a58, 0x00000000fa614c00, 0x00000000fa801000)
compacting perm gen total 21248K, used 2571K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
the space 21248K, 12% used [0x00000000fae00000, 0x00000000fb082c38, 0x00000000fb082e00, 0x00000000fc2c0000)
No shared spaces configured.
最大可用内存与最大内存少的原因:由于复制算法会将对象存活的对象复制到一块survivor中,所以最大可用内存为最大内存减去from区的大小。
2.新生代的设置
示例6---调整新生代的大小,观察它与GC次数的关系
package chapter3;
public class NewSizeDemo {
public static void main(String[] args) {
byte[] b = null;
for (int i = 0; i < 10; i++) {
b = new byte[1*1024*1024];
}
}
}
-Xmx20M -Xms20M-Xmn7M -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC |
[GC[DefNew: 2774K->1488K(5376K), 0.0022987 secs] 2774K->1488K(18688K), 0.0023433 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC[DefNew: 4641K->1024K(5376K), 0.0020457 secs] 4641K->1488K(18688K), 0.0020729 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC[DefNew: 4124K->1024K(5376K), 0.0009689 secs] 4589K->1488K(18688K), 0.0009919 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] Heap def new generation total 5376K, used 3162K [0x00000000f9a00000, 0x00000000fa100000, 0x00000000fa100000) eden space 3584K, 59% used [0x00000000f9a00000, 0x00000000f9c16a20, 0x00000000f9d80000) from space 1792K, 57% used [0x00000000f9f40000, 0x00000000fa040010, 0x00000000fa100000) to space 1792K, 0% used [0x00000000f9d80000, 0x00000000f9d80000, 0x00000000f9f40000) tenured generation total 13312K, used 464K [0x00000000fa100000, 0x00000000fae00000, 0x00000000fae00000) the space 13312K, 3% used [0x00000000fa100000, 0x00000000fa174258, 0x00000000fa174400, 0x00000000fae00000) compacting perm gen total 21248K, used 2569K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000) the space 21248K, 12% used [0x00000000fae00000, 0x00000000fb082580, 0x00000000fb082600, 0x00000000fc2c0000) No shared spaces configured. |
-Xmx20M -Xms20M -Xmn7M -XX:SurvivorRatio=8 -XX:+PrintGCDetails -XX:+UseSerialGC |
[GC[DefNew: 4860K->464K(6464K), 0.0021934 secs] 4860K->1488K(19776K), 0.0022451 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC[DefNew: 5708K->0K(6464K), 0.0021726 secs] 6732K->2512K(19776K), 0.0022013 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] Heap def new generation total 6464K, used 1081K [0x00000000f9a00000, 0x00000000fa100000, 0x00000000fa100000) eden space 5760K, 18% used [0x00000000f9a00000, 0x00000000f9b0e6d8, 0x00000000f9fa0000) from space 704K, 0% used [0x00000000f9fa0000, 0x00000000f9fa0088, 0x00000000fa050000) to space 704K, 0% used [0x00000000fa050000, 0x00000000fa050000, 0x00000000fa100000) tenured generation total 13312K, used 2512K [0x00000000fa100000, 0x00000000fae00000, 0x00000000fae00000) the space 13312K, 18% used [0x00000000fa100000, 0x00000000fa3741f0, 0x00000000fa374200, 0x00000000fae00000) compacting perm gen total 21248K, used 2569K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000) the space 21248K, 12% used [0x00000000fae00000, 0x00000000fb082580, 0x00000000fb082600, 0x00000000fc2c0000) No shared spaces configured. |
-Xmx20M -Xms20M -Xmn15M -XX:SurvivorRatio=8 -XX:+PrintGCDetails -XX:+UseSerialGC |
Heap def new generation total 13824K, used 11525K [0x00000000f9800000, 0x00000000fa700000, 0x00000000fa700000) eden space 12288K, 93% used [0x00000000f9800000, 0x00000000fa341728, 0x00000000fa400000) from space 1536K, 0% used [0x00000000fa400000, 0x00000000fa400000, 0x00000000fa580000) to space 1536K, 0% used [0x00000000fa580000, 0x00000000fa580000, 0x00000000fa700000) tenured generation total 5120K, used 0K [0x00000000fa700000, 0x00000000fac00000, 0x00000000fae00000) the space 5120K, 0% used [0x00000000fa700000, 0x00000000fa700000, 0x00000000fa700200, 0x00000000fac00000) compacting perm gen total 21248K, used 2569K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000) the space 21248K, 12% used [0x00000000fae00000, 0x00000000fb082580, 0x00000000fb082600, 0x00000000fc2c0000) No shared spaces configured.
|
-Xmx20M -Xms20M -XX:NewRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC |
[GC[DefNew: 4845K->464K(6144K), 0.0023531 secs] 4845K->1488K(19840K), 0.0024022 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC[DefNew: 5702K->0K(6144K), 0.0018932 secs] 6726K->2512K(19840K), 0.0019196 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] Heap def new generation total 6144K, used 1079K [0x00000000f9a00000, 0x00000000fa0a0000, 0x00000000fa0a0000) eden space 5504K, 19% used [0x00000000f9a00000, 0x00000000f9b0dca0, 0x00000000f9f60000) from space 640K, 0% used [0x00000000f9f60000, 0x00000000f9f60088, 0x00000000fa000000) to space 640K, 0% used [0x00000000fa000000, 0x00000000fa000000, 0x00000000fa0a0000) tenured generation total 13696K, used 2512K [0x00000000fa0a0000, 0x00000000fae00000, 0x00000000fae00000) the space 13696K, 18% used [0x00000000fa0a0000, 0x00000000fa3141f0, 0x00000000fa314200, 0x00000000fae00000) compacting perm gen total 21248K, used 2569K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000) the space 21248K, 12% used [0x00000000fae00000, 0x00000000fb082580, 0x00000000fb082600, 0x00000000fc2c0000) No shared spaces configured.
|
由此可以得出:新生代内存越大,GC次数越少;survivor适当减小,gc次数也会减少;所以应尽可能地将对象预留在新生代,减少老年代GC的次数。同时保证老年代有足够的空间进行担保。
3.堆溢出处理
示例7--堆溢出信息处理
package chapter3;
import java.util.Vector;
public class DumpOOM {
public static void main(String[] args) {
Vector<byte[]> v = new Vector<byte[]>();
for(int i=0;i<25;i++){
v.add(new byte[1*1024*1024]);
}
}
}
jvm参数:
-Xmx20M -Xms5M"-XX:OnOutOfMemoryError=D:/jdk1.7/bin/printstack.bat %p"-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:/program2015/jvm/src/a.dump
创建脚本工具printstack,是程序在堆栈溢出时尽心打印
printstack.bat:
输出结果:
java.lang.OutOfMemoryError: Java heap space
Dumping heap to D:/program2015/jvm/src/a.dump ...
Unable to create D:/program2015/jvm/src/a.dump: File exists
#
# java.lang.OutOfMemoryError: Java heap space
# -XX:OnOutOfMemoryError="D:/jdk1.7/bin/printstack.bat %p"
# Executing "D:/jdk1.7/bin/printstack.bat 7288"...
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at chapter3.DumpOOM.main(DumpOOM.java:9)
使用mat工具查看:
3.非堆内存参数设置
方法区设置、栈设置参考之前文章:https://blog.youkuaiyun.com/yinweicheng/article/details/80624259、https://blog.youkuaiyun.com/yinweicheng/article/details/80607402
直接内存配置
示例8--直接内存与堆内存的访问速度
代码片1:访问读写速度
package chapter3;
import java.nio.ByteBuffer;
public class AccessDirectBuffer {
public void directAccess(){
long starttime = System.currentTimeMillis();
ByteBuffer b = ByteBuffer.allocateDirect(500);
for (int i = 0; i < 100000; i++) {
for (int j = 0; j < 99 ; j++) {
b.putInt(j);
}
b.flip();
for (int j = 0; j < 99 ; j++) {
b.getInt();
}
b.clear();
}
long endtime = System.currentTimeMillis();
System.out.println("testDirectWrite:"+(endtime-starttime));
}
public void bufferAccess(){
long starttime = System.currentTimeMillis();
ByteBuffer b = ByteBuffer.allocate(500);
for (int i = 0; i < 100000; i++) {
for (int j = 0; j < 99 ; j++) {
b.putInt(j);
}
b.flip();
for (int j = 0; j < 99 ; j++) {
b.getInt();
}
b.clear();
}
long endtime = System.currentTimeMillis();
System.out.println("testBufferWrite:"+(endtime-starttime));
}
public static void main(String[] args) {
AccessDirectBuffer alloc = new AccessDirectBuffer();
alloc.bufferAccess();
alloc.directAccess();
alloc.bufferAccess();
alloc.directAccess();
}
}
输出结果:
使用-server模式后:
代码片2:内存空间申请
package chapter3;
import java.nio.ByteBuffer;
public class AccessDirecBuffer1 {
public void directAccess(){
long starttime = System.currentTimeMillis();
for (int i = 0; i < 200000; i++) {
ByteBuffer b = ByteBuffer.allocateDirect(1000);
}
long endtime = System.currentTimeMillis();
System.out.println("testDirectWrite:"+(endtime-starttime));
}
public void bufferAccess(){
long starttime = System.currentTimeMillis();
for (int i = 0; i < 200000; i++) {
ByteBuffer b = ByteBuffer.allocate(1000);
}
long endtime = System.currentTimeMillis();
System.out.println("testBufferWrite:"+(endtime-starttime));
}
public static void main(String[] args) {
AccessDirecBuffer1 alloc = new AccessDirecBuffer1();
alloc.bufferAccess();
alloc.directAccess();
alloc.bufferAccess();
alloc.directAccess();
}
}
输出结果:
可见,直接内存在进行访问读写操作时速度较快,而在申请内存空间时没有任何优势,所以直接内存适合申请次数少、访问频繁的场合。不适用于内存空间频繁申请的场合。
4.虚拟机的工作模式
server倾向于解释执行,会尝试收集更多的系统信息。