jmap -histo 16700>./log.txt
jmap -heap 16700
jmap -dump:format=b,file=eureka.hprof 16700
*程序
package com.jvm;
public class DeadLockTest {
private static Object lock1 = new Object();
private static Object lock2 = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (lock1) {
try {
System.out.println("thread1 begin");
Thread.sleep(5000);
} catch (InterruptedException e) {
}
synchronized (lock2) {
System.out.println("thread1 end");
}
}
}).start();
new Thread(() -> {
synchronized (lock2) {
try {
System.out.println("thread2 begin");
Thread.sleep(5000);
} catch (InterruptedException e) {
}
synchronized (lock1) {
System.out.println("thread2 end");
}
}
}).start();
System.out.println("main thread end");
}
}
查询相关的进程
jps
jsp
jinfo -flags 25984
jps
jinfo -sysprops 25984
jps
jstat -gc 25984
jps
jstat -gccapacity 25984
jps
jstat -gcnew 10832
jps
jstat -gcnewcapacity 10832
jps
jstat -gcold 10832
jps
jstat -gcoldcapacity 10832
jps
jstat -gcmetacapacity 10832
jps
jstat -gcutil 10832
jstat -gc 10832 2000 10000 >gc.log
通过这个文件可以分析出,七天内发生发生full gc次数和样GC次数
jmap -heap 10832
我们可以推测下full gc比minor gc还多的原因有哪些?
1、元空间不够导致的多余full gc
2、显示调用System.gc()造成多余的full gc,这种一般线上尽量通过-XX:+DisableExplicitGC参数禁用,如果加上了这个JVM启动参数,那么代码中调用System.gc()没有任何效果
3、老年代空间分配担保机制
jmap -histo 10832
wget https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar
help查询相关的命令
dashboard整个进程的运行情况,线程、内存、GC、运行环境信息
thread 查询当前类运行的线程,thread id查询具体的线程信息
thread -b 查询死锁的线程
jad com.jvm.Arthas 对类进行反编译
ognl @com.jvm.Arthas@hashSet 查看变量的值
** 查看变量的值**
ognl ‘@[email protected]("123")’
-Xloggc:d:/gc-cms-%t.log -Xms50M -Xmx50M -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M -XX:+PrintGCDetails -XX:+PrintGCDateStamps
-XX:+PrintGCTimeStamps -XX:+PrintGCCause -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M
-XX:+UseParNewGC -XX:+UseConcMarkSweepGC
-Xloggc:d:/gc-g1-%t.log -Xms50M -Xmx50M -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M -XX:+PrintGCDetails -XX:+PrintGCDateStamps
-XX:+PrintGCTimeStamps -XX:+PrintGCCause -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M -XX:+UseG1GC
https://gceasy.io
可以通过以上连接可以简单分析GC日志的相关内容
java -XX:+PrintFlagsInitial 表示打印出所有参数选项的默认值
java -XX:+PrintFlagsFinal 表示打印出所有参数选项在运行程序时生效的值
Class常量池与运行时常量池
Class常量池可以理解为是Class文件中的资源仓库。 Class文件中除了包含类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池(constant pool table),用于存放编译期生成的各种字面量(Literal)和符号引用(Symbolic References)。
字面量就是指由字母、数字等构成的字符串或者数值常量
int a = 1;
int b = 2;
int c = "abcdefg";
int d = "abcdefg";
说明:以上的1,2,abcdefg都属于字面量
上面的a,b就是字段名称,就是一种符号引用,还有Math类常量池里的 Lcom/tuling/jvm/Math 是类的全限定名,main和compute是方法名称,()是一种UTF8格式的描述符,这些都是符号引用。
这些常量池现在是静态信息,只有到运行时被加载到内存后,这些符号才有对应的内存地址信息,这些常量池一旦被装入内存就变成运行时常量池,对应的符号引用在程序加载或运行时会被转变为被加载到内存区域的代码的直接引用,也就是我们说的动态链接了。例如,compute()这个符号引用在运行时就会被转变为compute()方法具体代码在内存中的地址,主要通过对象头里的类型指针去转换直接引用。
1.字符串的分配,和其他的对象分配一样,耗费高昂的时间与空间代价,作为最基础的数据类型,大量频繁的创建字符串,极大程度地影响程序的性能
2.JVM为了提高性能和减少内存开销,在实例化字符串常量的时候进行了一些优化
为字符串开辟一个字符串常量池,类似于缓存区
创建字符串常量时,首先查询字符串常量池是否存在该字符串
存在该字符串,返回引用实例,不存在,实例化该字符串并放入池中
1、在 JDK 1.6 中,调用 intern() 首先会在字符串池中寻找 equal() 相等的字符串,假如字符串存在就返回该字符串在字符串池中的引用;假如字符串不存在,虚拟机会重新在永久代上创建一个实例,将 StringTable 的一个表项指向这个新创建的实例。
2、在 JDK 1.7 (及以上版本)中,由于字符串池不在永久代了,intern() 做了一些修改,更方便地利用堆中的对象。字符串存在时和 JDK 1.6一样,但是字符串不存在时不再需要重新创建实例,可以直接指向堆上的实例。
String s1="Hello";
String s2="Hello";
String s3="He"+"llo";
System.out.println(s1==s2);
System.out.println(s1==s3);
分析:在编译期,当s1创建的时候,java程序将会在堆里面查看是否有’Hello’这个字符串,没有将会在堆内存的字符串常量池中创建’Hello’字符串,当s2创建时,会直接查看堆字符串常量池中是否有,此时已经存在,直接引用堆里面地址,s3创建时,内存把字符串直接优化为’Hello’,所有三个值都相等
String s0="zhuge";
String s1=new String("zhuge");
String s2="zhu" + new String("ge");
String s3=new String("zhuge");
System.out.println( s0==s1 );
System.out.println( s0==s2 );
System.out.println( s1==s2 );
System.out.println( s1==s3 );
分析:在编译期,s0指向的是堆里面的字符串常量池,s1new字符串会在堆里面非字符串常量池开辟一个对象空间,s2会在字符串常量池中创建一个zhu的常量,同时在会在堆里面非字符串常量池开辟一个对象空间,s31new字符串会在堆里面非字符串常量池开辟一个对象空间,所以结果都为false
3. 字符串和数字拼接字符比较
String s1="a"+129;
String s2="a129";
System.out.println(s1==s2);
分析:在编译期,当s1创建的时候,java程序将会在堆里面查看是否有’a129’这个字符串,没有将会在堆内存的字符串常量池中创建’a129’字符串,当s2创建时,会直接查看堆字符串常量池中是否有,此时已经存在,直接引用堆里面字符串常量池地址
String a = "ab";
String bb = "b";
String b = "a" + bb;
System.out.println(a == b);
分析:在编译器a会在字符串常量池中生成’ab’,bb会在池中生成’b’,但是b无法在编译器生成,会在运行器生成,动态生成地址给b,所以为false
String a = "ab";
final String bb = "b";
String b = "a" + bb;
System.out.println(a == b);
分析:和示例4中唯一不同的是bb字符串加了final修饰,对于final修饰的变量,它在编译时被解析为常量值的一个本地拷贝存储到自己的常量池中或嵌入到它的字节码流中。所以此时的"a" + bb和"a" + "b"效果是一样的。故上面程序的结果为true。
public static void main(String[] args) {
String a = "ab";
final String bb = getBB();
String b = "a" + bb;
System.out.println(a == b); // false
}
private static String getBB()
{
return "b";
}
分析:bb的引用来至于方法,所以在编译期无法确定值,在运行期会把’a’和方法返回动态分配给b,所以此时为false
String s = "a" + "b" + "c";
String a = "a";
String b = "b";
String c = "c";
String s1 = a + b + c;
StringBuilder temp = new StringBuilder();
temp.append(a).append(b).append©;
String s = temp.toString();
String str2 = new StringBuilder("计算机").append("技术").toString();
System.out.println(str2 == str2.intern());
String str1 = new StringBuilder("ja").append("va").toString();
System.out.println(str1 == str1.intern());
分析:"计算机技术"在字符串常量没有,所以str2 和 str2.intern()返回的是堆里面非字符串常量对象,所以为true,str1 返回的是堆中非字符串常量池中的引用,而str1.intern(),java关键字在创建的时候,就已经创建了,返回的是字符串常量池中的对象。所以为false
System.out.println("------Integer------");
Integer i1 = 127;
Integer i2 = 127;
System.out.println(i1 == i2);
Integer i3 = 128;
Integer i4 = 128;
System.out.println(i3 == i4);
System.out.println("------Double------");
Double i5 = 127d;
Double i6 = 127d;
System.out.println(i5 == i6);
Double i7 = 128d;
Double i8 = 128d;
System.out.println(i7 == i8);
System.out.println("------Short------");
Short s1 = 127;
Short s2 = 127;
System.out.println(s1 == s2);
Short s3 = 128;
Short s4 = 128;
System.out.println(s3 == s4);
System.out.println("------Long------");
Long l1 = 127L;
Long l2 = 127L;
System.out.println(l1 == l2);
Long l3 = 128L;
Long l4 = 128L;
System.out.println(l3 == l4);
System.out.println("------Float------");
Float f1 = 127f;
Float f2 = 127f;
System.out.println(f1 == f2);
Float f3 = 128f;
Float f4 = 128f;
System.out.println(f3 == f4);
System.out.println("------Boolean------");
Boolean i9=true;
Boolean i10=true;
System.out.println(i9 == i10);
分析:在对象池中编译时会初始化一批对象,对象范围在Integer(-128-127之间),Short(-32768~32767),Long(-128-127之间),Fload,Double无初始化值