jvm基础结构
程序计数器PC(Program Counter)
- 每个线程创建的时候都会有一个PC寄存器
- pc 寄存器的内容重视指向下一条被执行指令的地址,这里的地址可以时一个本地指针,也可以是在方法区中相对应该方法起始指针的偏移量
- 每个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区中的方法字节码(用来存储指向下一条指令的地址,也就是即将要执行的命令代码),由执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不计;
- 这块内存区域很小,意识当前线程执行的字节码的信号指示器,字节码解释通过改变这个计数器的值来选取下一条需要执行的字节码指令。
- 如果执行的时一个Native方法,那这个计数器是空的。
方法区
- 保存封装类信息(常量池、字段、方法信息、方法字节码)
- 通常和永久区(Perm)关联在一起
java堆
- 和程序开发密切相关
- 应用系统对象都保存在java堆中
- 所有线程共享java堆
- 对分代GC来说,堆也是分代的
- GC的主要工作区
java栈
- 线程私有
- 栈由一系列帧组成(java栈也叫栈帧)
- 栈保存一份方法的局部变量(包含传入参数)、操作数栈、常量池指针
- 每一次方法调用创建一个栈,并开始压栈
java没有寄存器,所有参数传递都是操作数栈完成
public static int add(int a,int b){
int c=0;
c = a+b;
return c;
}
0: iconst_0 //0压栈
1: istore_2 //弹出int,存放与局部变量2
2: iload_0 //把局部变量0压栈
3: iload_1 //局部变量1压栈
4: iadd //弹出2个变量,求和,结果压栈
5 istore_2 //弹出的结果防御局部变量2
6: iload_2 //局部变量2压栈
7: ireturn //返回
java栈之栈上分配
public class StackTest{
public static void call(){
byte[] b = new byte[10];
for(int i=0;i<b.length;i++){
b[i]=1;
}
}
public static void main(String[] args){
for(int i=0;i<1000000;i++){
call();
}
}
}
- 启动jvm参数1
-Xmx10m -Xms10m -xx:+DoEscapeAnalysis -XX:+PrintGC
栈上分配对象,不需要回收,也就不启动GC
- 启动jvm参数2
-Xmx10m -Xms10m -xx:-DoEscapeAnalysis -XX:+PrintGC
结果:打印GC
对中new对象时,需要结束后对对象删除,会启动GC回收,否则溢出
总结:
- 小对象,在没有逃逸(也就是在其他线程中也要使用)的情况下,可以直接分配在栈上。
- 直接分配在站上,可以自己回收,减轻GC压力。
- 大对象或者逃逸对象无法在栈上分配。
内存模型
每一个线程都有一个工作内存和独立内存。
- 工作内存存放主内存变量指的拷贝
- 当数据从主内存复制到工作内存时,必须有两个动作
- 由主内存执行读取操作;
- 由工作内存执行响应的load操作
- 当数据从工作内存复制到主内存时,必需要出来那个两个动作
- 由于工作内存执行的存储操作
- 由朱内存执行相应的write操作
每一个操作都是院子的,即操作执行期间不会被中断。对于变量,一个线程中更新的指,不会马上反应到其他变量中。如果要在其他线程中立即可见需要使用volatile关键字。但volatile不能代替锁,一般任务volatile性能好(不绝对)切线程不安全。
指令重排序的基本原则(编译器为了更好的性能)
- 程序排序原则:一个线程内存保证语义的串行性
- volatile规则:volatile变量的写,先发生于读
- 锁规则:解锁必须发生在加锁之后
- 传递性:A优先于B,B优先于C,那么A必然优先于C
- 线程的start方法先于它的每一个动作
- 线程的所有操作先于线程的终结
- 线程终结后的程序不应该再被执行
- 对象的构造函数执行结束优先于finally()方法
解释运行与编译运行
解释运行
- 解释执行以解释方式运行字节码
- 解释执行的意思时:读一句,执行一句
编译运行(JIT)
- 字节码编译成机器码
- 直接执行机器码
- 运行时编译
- 编译后性能有数量级的提升
Trace跟踪参数
-X:non-standard(非标准参数),这些参数不是虚拟机规范规定的。因此,不是所有的vm的实现都支持这些配置参数,例如:-Xms -Xmx -Xmn -Xss
-XX:not-stable(不稳定参数),这些参数时虚拟机规范中规定的。这些单数指定虚拟机实例在运行时的各种行为,从而对虚拟机的运行时性能有很大影响。如:-XX:SurvivorRatio -XX:+UseRarNewGc;
-X和-XX两种参数都可能随着JDK版本的变化而变更,这些参数可能会被废弃掉,有些参数的功能会发生改变,但是JDK官方不会通知开发者的这些变化,需要试着用者注意。
- -verbose:gc :在控制台打印GC重要的简洁信息
- -XX:+printGC 在控制台打印详细的GC信息
- XX:+printGCTimeSamps: 打印GC的时间戳
- Xloggc:/root/logs/gc.log :指定GClog的位置为/root/logs/gc.log,以文本输出,帮助开发分析
- -XX:+PrintHeapAtGC: 每一次gc后,都打印堆的信息,打印gc前后的堆信息
- XX:+TraceClassLoading 监控类的加载
堆的分配
- -Xmx -Xms 指定最大堆和最小堆。 -Xmx20m -Xms5m 运行代码:
System.out.print("Xmx=");
System.out.println(Runtime.getRuntime().maxMemory()/1024/2014+"M");
System.out.print("free mem=");
System.out.println(Runtime.getRuntime().freeMemory()/1024/2014+"M");
System.out.print("total mem=);
System.out.println(Runtime.getRuntime().totalMemory()/1024/2014+"M");
Xmx = 19.3M
free mem=4.25M
total mem=4.875M
- Xmn: 设置年轻代大小
- -XX:NewRatio 新生代(eden+2*s)和老年代(不包含永久区)的比值。-8表示两个Survivor:eden=2:8,一个Survivor占年轻代的1/10;
- -XX:+HeapDumpOnOutOfMemoryError OOM时到处堆文件
- -XX:+HeapDumpPath:/root/logs/oom.log 导出OOM文件的路径
- -XX:OnOutOfMEmoryError 在oom时执行一个脚本,可以发送邮件,或者重启服务。
根据实际大小调整新生代和幸存代的大小:官方推荐新生代占2/8,幸存代占新生代的1/10。在OOM时,记得dump出栈,确保可以排查现场问题。
永久区的参数分配
- -XX:PermSize与-XX:MaxPermSize设置永久区的初始空间的最大空间,他们表示一个系统可容纳多少个类型。
- 永久区溢出,一样会抛出OOM
栈大小分配
- -Xss:
- 通常只有几百k
- 决定函数调用的深度
- 每个线程都有独立的栈空间
- 局部变量、参数分配在栈上
性能监控
- vmstat
可统计系统的cpu 内存 swap io等情况
语法:vmstat [采样平率] [采样次数]
- pidstat
细致观察进程(监控CPU、IO、内存)
语法:pidstat -p [进程号]
参数:-u 1 3 -u监控cpu, -u 1 3 每1秒采集共三次 ,如 pidstat -p 1025 -u 1 3
参数:-t 显示线程,如 pidstat -p 1025 -u 1 3 -t
参数:-d 检查磁盘IO ,如 pidstat -p 1025 -u 1 3 -t -d
- jps:列出java进程
参数 | 示例 | 说明 |
---|---|---|
-q | jps -q | 只输出进程ID,不输出类名 |
-m | jps -m | 输出传递给java进程(主函数)的参数 |
-l | jps -m -l | 输出主函数的完成路径 |
-v | jps -m -l -v | 显示传递给JVM的参数 |
- jinfo:用来查看正常运行的java应用的扩展参数,甚至支持运行时,修改部分参数
参数 | 示例 | 说明 |
---|---|---|
-flag | jinfo -flag MaxTenuringThreshold 2250 输出:-XX:MaxTenuringThreshold=15 | 打印指定的jvm参数 |
-flag name=value | jinfo -flag MaxTenuringThreshold=15 2250 | 设置jvm参数 |
-flag ±name | jinfo -flag +PrintGCDetails 2250 | 设置jvm参数 |
- jmap
生成java应用程序的堆快照和对象统计信息:
jmap -histo 2250
- jstack
打印线程dump
jstack -l 打印锁信息
jstack -m 打印java和native的帧信息