谈谈对JVM的理解(二)

1、JVM启动流程
  Java XXX,执行java命令后
  装置配置,根据当前路径和系统版本寻找jvm.cfg文件
  根据配置文件寻找JVM.dll,JVM.dll为JVM主要实现
  初始化JVM,获得相关native接口,其中JNIEnv接口,该接口为JVM接口,findClass等操作通过它实现
  找到main方法并运行
2、堆,栈,方法区
  堆:主要保存成员变量,对象,常量池(JDK7开始,之前放在方法区),所有线程共享堆,堆是分代的
  栈:保存对象的引用,一个方法内的局部变量,常量池指针,线程私有,由一系列帧组成,每一次方法调用创建一个帧,并压栈
  方法区:保存Class对象的元数据(字段,方法信息,方法字节码)
  Java没有寄存器,所有参数传递使用操作数栈
  栈上分配:小对象(一般几十个bytes),在没有逃逸的情况下,可以直接分配在栈上,直接分配在栈上,可以自动回收,减轻GC压力,大对象或者逃逸对象无法栈上分配。
3、主内存,线程工作内存
  主内存:所有线程共享的内存
  线程工作内存:当前线程独享的内存,其它线程不可见
  当数据从主内存复制到工作存储时,必须出现两个动作:第一、由主内存执行的读(read)操作;第二、由工作内存执行的相应的load操作;当数据从工作内存拷贝到主内存时,也出现两个操作:第一个、由工作内存执行的存储(store)操作;第二、由主内存执行的相应的写(write)操作。
  每一个操作都是原子的,即执行期间不会被中断
  对于普通变量,一个线程中更新的值,不能马上反应在其他变量中
4、volatile,synchronized,final
  volatile:被volatile声明的变量,当一个线程修改了该变量,其他线程可以立即知道。当一个线程正在工作,需要用到另外一个线程修改某个变量后的值,对该变量用volatile声明对好。
  synchronized:被它声明的方法,代码块,对象,会加上锁,在将方法执行完,代码块执行完,对象中变量值写回主内存之前,其它线程访问这些信息处于等待状态,只有真正执行完,变量值写回主内存后,才会释放锁。加锁和释放锁比较消耗性能。
  final:被final声明的变量是不可变的,被final声明的对象,引用不可变,一旦初始化完成,其它线程就可见。
5、指令重排的基本原则
  程序顺序原则:一个线程内包装语义的串行性
  volatile规则:volatile变量的写,先发生于读
  锁规则:解锁必然发生在随后的加锁前
  传递性:A先于B,B先于C,那么A必然先于C
  线程的start方法先于它的每一个动作
  线程的所有操作先于线程的终结(Thread.join())
  线程的中断(interrupt())先于被中断线程的代码
  对象的构造函数执行结束先于finalize()方法
6、解释运行和编译运行
  解释运行:解释运行以解释方式运行字节码,读一句执行一句
  编译运行:将字节码编译成机器码,直接执行机器码,运行时编译,编译后性能有数量级的提升。
7、常用JVM配置参数
  Trace跟踪参数
    -verbose:gc
    -XX:printGC 打印gc工作的简单信息
    -XX:+PrintGCDetails 打印gc工作的详细信息
    -XX:+PrintGCTimeStamps 打印gc工作发生的时间戳
    -Xloggc:log/gc.log 指定GC log的位置,以文件输出
    -XX:+PrintHeapAtGC 每一次GC后,都打印堆信息
    -XX:+TraceClassLoading 监控类的加载
  堆的分配参数
    -Xmx -Xmx 指定最大堆和最小堆
    Runtime.getRuntime().maxMemory()得到可用最大的内存
    Runtime.getRuntime().freeMemory得到空闲的内存
    java运行尽量在最小堆内存中 ,如果空间不够就会向上去取,如果达到最大上限就会报OOM错误
    -Xmn 设置新生代大小
    -XX:NewRatio
      新生代(eden+2*s)和老年代(不包含永久区)的比值
      4表示新生代:老年代=1:4,即年轻代占堆的1/5
    -XX:SurvivorRatio
      设置两个Survivor区和eden的比
      8表示两个Survivor:eden=2:8,即一个Survivor占年轻代的1/10
    -XX:+HeapDumpOnOutOfMemoryError OOM时导出堆到文件
    -XX:+HeapDumpPath 导出OOM的路径
    -XX:OnOutOfMemoryError 在OOM时,执行一个脚本 ,-XX:OnOutOfMemorryError=脚本的位置 %p(%p指的是当前线程),当程序OOM时,就会执行这个脚本,将当前线程的信息传送过去,也可以在OOM时,发送邮件通知,或者重启程序。
    总结:
      1、根据实际情况调整新生代和幸存代的大小
      2、官方推荐新生代占堆的3/8
      3、幸存代占新生代的1/10
      4、在OOM时,记得Dump出堆,确保可以排查现场问题
    永久区分配参数
      -XX:PermSize -XX:MaxPermSize 设置永久区的初始空间和最大空间,表示一个系统可以容纳多少个类型
      永久区的内存不够,也会导致OOM
  栈的分配参数
    -Xss 通常只有几百K(一般比较小,如果调大,可容纳的线程数量就会变小),决定了函数调用的深度,每个线程都有独立的栈空间,局部变量、参数、分配在栈上。

猜你喜欢

转载自blog.csdn.net/qq_38019655/article/details/82670224