美文网首页
第二周 JVM运行机制

第二周 JVM运行机制

作者: 空谷幽心 | 来源:发表于2018-07-15 22:52 被阅读37次

笔记

  1. JVM启动流程
    启动过程如下图所示:


    bootup.png

    注释:

    • jvm.cfg的用途:Controls the JVMs which may be picked with startup flags when invoking java or javac
      当前系统的默认配置是:
    ```bash
        # List of JVMs that can be used as an option to java, javac, etc.
        # Order is important -- first in this list is the default JVM.
        # NOTE that this both this file and its format are UNSUPPORTED and
        # WILL GO AWAY in a future release.
        #
        # You may also select a JVM in an arbitrary location with the
        # "-XXaltjvm=<jvm_dir>" option, but that too is unsupported
        # and may not be available in a future release.
        #
        -server KNOWN
        -client IGNORE
    ```
    
    • JVM.dll
      在linux上是libjvm.so,路径:/usr/lib/jvm/java-8-oracle/jre/lib/amd64/server/libjvm.so。并且没有client目录,表面在linux都是server模式。
  2. JVM基本结构
    总的结构示意图如下所示:

    structure.png
    说明:
    • PC寄存器
      每个线程拥有一个,指向下一条指令的地址,执行native方法的时值为undefined
      这表明线程是执行指令和CPU调度的基本单位。再复杂的系统,也是从main线程扩展起来的。
    • 方法区
      JDK7放在Perm区,JDK8放在MetaSpace区。
      主要用于存放加载的类信息,包括:
      1. 类型的常量值(JDK8已经移到Heap上)。
      2. 类的属性(字段)和方法信息。
      3. 方法字节码。
    • Java堆
      1. 应用新建的对像、数组以及常量等都存放在Java堆上。
      2. Java堆可以被所有线程访问,是最重要的内存共享区域。
      3. 在分代GC算法下,Java堆是分代的。典型的分代如下图:
        heap_gen.png
    • Java栈
      1. 线程私有。
      2. 存放方法调用相关的数据,包括参数、局部变量以及返回值。对象、数组这些分配在堆上,栈里只保留引用。
      3. 栈上也可以分配对象。
        前提条件:对象很小(几十byte);开启逃逸分析(-XX:+DoEscapeAnalysis)
        优点:对象在不逃逸的情况下,直接分配在栈上,方法调用结束对象即回收,不增加堆的负担。
        缺点:要求对象很小,适用场景有限,用处不大,所以这项技术也没有流行起来。
        验证:跑了资料中的代码,在启用逃逸分析后,只发生了很少几次GC。如果禁用逃逸分析,会发生大量的GC。
         public class OnStackTest {
      
             public static void alloc(int n) {
                 byte[] b = new byte[n];
                 b[0] = 1;
             }
      
             public static void main(String[] args) throws InterruptedException {
                 for (int i = 0; i < 500000000; i++) {
                     alloc(2);
                 }
             }
         }
      
      开启逃逸分析:
         dalton@fish ~$ java -Xmx10m -Xms10m -XX:+DoEscapeAnalysis -XX:+PrintGC OnStackTest 
         [GC (Allocation Failure)  2048K->376K(9728K), 0.0012055 secs]
         [GC (Allocation Failure)  2424K->360K(9728K), 0.0018963 secs]
      
      禁用逃逸分析:
         dalton@fish ~$ java -Xmx5m -Xms5m -XX:-DoEscapeAnalysis -XX:+PrintGC OnStackTest 
         [GC (Allocation Failure)  1024K->408K(5632K), 0.0091321 secs]
         [GC (Allocation Failure)  1432K->352K(5632K), 0.0016615 secs]
         [GC (Allocation Failure)  1376K->352K(5632K), 0.0011853 secs]
         [GC (Allocation Failure)  1376K->352K(5632K), 0.0017079 secs]
         [GC (Allocation Failure)  1376K->368K(5632K), 0.0017921 secs]
         [GC (Allocation Failure)  1392K->352K(5632K), 0.0010113 secs]
         [GC (Allocation Failure)  1376K->292K(5632K), 0.0010523 secs]
         [GC (Allocation Failure)  1316K->292K(5632K), 0.0020685 secs]
         [GC (Allocation Failure)  1316K->292K(5632K), 0.0019285 secs]
         [GC (Allocation Failure)  1316K->292K(5632K), 0.0020571 secs]
         [GC (Allocation Failure)  1316K->292K(5632K), 0.0030951 secs]
         [GC (Allocation Failure)  1316K->292K(5632K), 0.0014476 secs]
         [GC (Allocation Failure)  1316K->292K(5632K), 0.0007672 secs]
      
      默认会开启逃逸分析的哦
         dalton@fish ~/a_dev/d_java/perf $ java -Xmx10m -Xms10m -XX:+PrintGC OnStackTest 
         [GC (Allocation Failure)  2048K->392K(9728K), 0.0012030 secs]
         [GC (Allocation Failure)  2440K->384K(9728K), 0.0018770 secs]
      
  3. 内存模型
    内存模型,即我们常说的JMM,描述了内存共享变量的读写及可见性。要点:

    • 每个线程都有自己的工作内存。
    • 共享变量存放在主内存中,与各线程的工作内存独立。
    • 线程要读取或写入共享变量,各自都要经过另个步骤。
      读:read,load。read是从共享内存中读到工作内存;load是从工作内存中加载到变量。
      写:store,write。store是从变量保存到工作内存;write是从工作内存写到共享内存。
      这个过程的示意图如下:


      jmm.png
    • 要做到线程间可见(或同步),可以用以下几种方法:
      1. 用volatile关键字修饰变量。
      2. 用synchronized关键字修饰方法或代码块。线程在进入synchronized代码块时会从共享内存中读取变量值,离开时会写入变量值。这就保证了执行完synchronized代码块后对共享变量的修改是可见的。
      3. 使用常量。
  4. 指令重排
    编译器和执行器都会基于一定的优化原则对指令进行重排。其结果是指令的实际执行顺序不一定是我们代码中看到的顺序。重排是一种重要的优化手段,但同时也增加了线程间同步的困难。因重排的条件比较复杂,我们倒是可以记住不发生重排的几种情况:

    • 写后读
    • 读后写
    • 写后写

    编译器不考虑多线程间的语义。即它不会考虑多线程间的同步。

    指令重排的基本原则:

    • 程序顺序原则:一个线程内保证语义的串行性
    • volatile规则:volatile变量的写,先发生于读
    • 锁规则:解锁(unlock)必然发生在随后的加锁(lock)前
    • 传递性:A先于B,B先于C 那么A必然先于C
    • 线程的start方法先于它的每一个动作
    • 线程的所有操作先于线程的终结(Thread.join())
    • 线程的中断(interrupt())先于被中断线程的代码
    • 对象的构造函数执行结束先于finalize()方法
  5. 编译与解释运行的概念
    解释运行:读一句执行一句字节码。
    编译运行:将字节码编译成机器码,然后执行。

理解与思考

相关文章

  • 第二周 JVM运行机制

    笔记 JVM启动流程启动过程如下图所示:bootup.png注释:jvm.cfg的用途:Controls the ...

  • 深入JVM内核 目录

    深入JVM内核 目录 深入JVM内核1 初识JVM深入JVM内核2 JVM运行机制深入JVM内核3 常用JVM配置...

  • JVM学习笔记一

    JVM运行机制 JVM的启动过程 JVM的基本结构 JVM通过ClassLoader将class文件加载到内存中 ...

  • Java Review (Java开发环境)

    @ 高级语言运行机制编译型语言解释型语言 Java运行机制和JVM编写编译运行 JDK JREJDK、JRE与JV...

  • Java Review (Java开发环境)

    @ 高级语言运行机制编译型语言解释型语言 Java运行机制和JVM编写编译运行 JDK JREJDK、JRE与JV...

  • 多线程的锁升级原理

    什么是锁升级(锁膨胀)? JVM优化synchronized的运行机制,当JVM检测到不同的竞争状态时,就会根据需...

  • 2018-05-26 Java学习随笔4——有关Java语言、开

    Java的运行机制和JVM Java语言的运行机制大致如上图所示,Java源文件(源代码等.java文件)先经过J...

  • 1.JVM运行机制的整体脉络

    JVM系列 1.JVM运行机制的整体脉络2.JVM的分代模型及对象流动3.常见的垃圾回收器及算法4.ParNew和...

  • 4.ParNew和CMS的工作原理

    JVM系列 1.JVM运行机制的整体脉络2.JVM的分代模型及对象流动3.常见的垃圾回收器及算法4.ParNew和...

  • jvm运行机制

    当代码写的多了,有时容易产生疑惑,这段代码容不容易产生内存溢出呢?该怎么写才更为面向对象利于后期扩展,充分解耦。需...

网友评论

      本文标题:第二周 JVM运行机制

      本文链接:https://www.haomeiwen.com/subject/hjjmpftx.html