第二周 JVM运行机制

笔记

  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. 编译与解释运行的概念
    解释运行:读一句执行一句字节码。
    编译运行:将字节码编译成机器码,然后执行。

理解与思考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容

  • 垃圾回收算法具体实现 翻译原文 => plumbr Java GC handbook 前文参见: Java垃圾回收...
    foxracle阅读 2,846评论 0 15
  • 作者:一字马胡 转载标志 【2017-11-12】 更新日志 日期更新内容备注 2017-11-12新建文章初版 ...
    beneke阅读 2,184评论 0 7
  • 内存溢出和内存泄漏的区别 内存溢出:out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,...
    Aimerwhy阅读 730评论 0 1
  • JVM架构图分析 JVM被分为三个主要的子系统 (1)类加载器子系统(2)运行时数据区(3)执行引擎 1. 类加载...
    匆匆岁月阅读 1,013评论 1 19
  • 我们常期待一份纯粹的吸引,年少的不顾一切,迫不及待想与你为伍。那时我,喜欢秀发拂过手腕时你的姿态,喜欢窗边叼着烟的...
    木木木t阅读 285评论 0 0