阿里巴巴Arthas详解
Arthas使用场景
- 是否有一个全局视角来查看系统的运行状况?
- 为什么 CPU 又升高了,到底是哪里占用了 CPU ?
- 运行的多线程有死锁吗?有阻塞吗?
- 程序运行耗时很长,是哪里耗时比较长呢?如何监测呢?
- 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
- 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
- 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
- 有什么办法可以监控到 JVM 的实时运行状态?
GC日志详解
对于java应用我们可以通过一些配置把程序运行过程中的gc日志全部打印出来
然后分析gc日志得到关键性指标,分析GC原因,调优JVM参数。
# 运行程序加上对应gc日志
java -jar -Xloggc:./gc-%t.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+PrintGCCause
-XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M microservice-eureka-server.jar
#jdk17
java -jar -Xlog:gc:./gc-%t.log web-1.0-SNAPSHOT.jar
GC日志分析工具
上面的这些参数,能够帮我们查看分析GC的垃圾收集情况。但是如果GC日志很多很多,成千上万行。就算你一目十行,看完了,脑子也是一片空白。所以我们可以借助一些功能来帮助我们分析,这里推荐一个gceasy(gceasy.io),可以上传gc文件,然后他会利用可视化的界面来展现GC情况。
JVM参数汇总查看命令
java -XX:+PrintFlagsInitial 表示打印出所有参数选项的默认值
java -XX:+PrintFlagsFinal 表示打印出所有参数选项在运行程序时生效的值
Class常量池与运行时常量池详解
Class常量池可以理解为是Class文件中的资源仓库。 Class文件中除了包含类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池(constant pool table),用于存放编译期生成的各种字面量(Literal)和符号引用(Symbolic References)。
字面量
字面量就是指由字母、数字等构成的字符串或者数值常量
符号引用
符号引用是编译原理中的概念,是相对于直接引用来说的。主要包括了以下三类常量:
- 类和接口的全限定名
- 字段的名称和描述符
- 方法的名称和描述符
常量池 → 运行时被加载到内存后 → 运行时常量池,对应的符号引用 → 运行时 → 动态链接。
例如,compute()这个符号引用在运行时就会被转变为compute()方法具体代码在内存中的地址,主要通过对象头里的类型指针去转换直接引用。
字符串常量池
字符串常量池的设计思想
- 字符串的分配,和其他的对象分配一样,耗费高昂的时间与空间代价,作为最基础的数据类型,大量频繁的创建字符串,极大程度地影响程序的性能
- JVM为了提高性能和减少内存开销,在实例化字符串常量的时候进行了一些优化
- 为字符串开辟一个字符串常量池,类似于缓存区
- 创建字符串常量时,首先查询字符串常量池是否存在该字符串
- 存在该字符串,返回引用实例,不存在,实例化该字符串并放入池中
三种字符串操作
- 直接赋值字符串
- 这种方式创建的字符串对象,只会在常量池中。
- new String()
- 这种方式会保证字符串常量池和堆中都有这个对象,没有就创建,最后返回堆内存中的对象引用。
- intern方法
- 如果池已经包含一个等于此String对象的字符串(用equals(oject)方法确定),则返回池中的字符串。否则,将intern返回的引用指向当前字符串 s1
字符串常量池位置
通过 OOM 程序验证字符串常量池在的位置
/*
vm options: -Xms6M -Xmx6M -XX:MetaspaceSize=6M -XX:MaxMetaspaceSize=6M
* */
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
for (int i = 0; i < 10000000; i++) {
String str = String.valueOf(i).intern();
list.add(str);
}
}
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
字符串常量池设计原理
字符串常量池底层是hotspot的C++实现的,底层类似一个 HashTable, 保存的本质上是字符串对象的引用。
八种基本类型的包装类和对象池
因为一般这种比较小的数用到的概率相对较大
所以Byte,Short,Integer,Long,Character这5种整型的包装类在对应值[-128,127]时会使用对象池。
Integer i1 = 127;//-128~127
Integer i2 = Integer.valueOf(127);
System.out.println(i1 == i2);