JVM 面试必备(下)

类加载的5个过程

类加载的本质

将描述类的数据 从Class文件加载到内存并且对数据进行校验 转换解析和初始化 最终新城虚拟机直接使用java使用类型

类加载过程

  1. 加载

    • 作用

      将外部的Class文件加载到虚拟机并且存储到方法区内

    • 具体流程

      1. 通过类名的全限定名来获取定义此类的二进制数据

      2. 将这个字节流所代表的的静态存储结构转化为方法区的运行时数据结构

      3. 在内存中生成一个代表这个类的java.lang.class对象 作为方法区该类的各种数据的访问入口

    • 注意

      • 数组类是通过java虚拟机直接创建 不通过类的加载机制
  2. 验证

    • 作用 确

      保加载进来的class文件包含的信息符合jvm的要求

    • 具体流程

      1. 文件格式的校验

      2. 元数据校验

      3. 字节码校验

      4. 符号引用校验

  3. 准备

    • 作用

      为类变量分配内存 并且设置类变量的初始值

    • 具体流程

      1. 为类的static变量在方法区中分配内存

      2. 将上述变量的初始值设置为0

    • 注意

      • 实例变量不在该阶段分配内存

      • 若该类为常量(final修饰) 直接复制开发者定义的值

  4. 解析

    • 作用

      将常量池内的符号引用转为直接引用

    • 具体流程

      解析对象(类/接口) 方法 (类方法 接口方法 方法类型 方法句柄) 字段

    • 注意

      • 实例变量不在该阶段分配内存

      • 因为类方法和私有方法符合 "编译器可知 , 运行期不可变" 的要求 即不会被继承或者重写 所以适合类加载过程进行解析

      • 若类变量为常量 (final 修饰 ) 则直接赋值开发者定义的值

  5. 初始化

    • 作用

      初始化类变量 静态语句块

    • 具体流程

      1. 生成类构造器 clinit() 即合并所有类变量和静态语句块

      2. 执行clinit()方法

    • 注意

      • 类构造器clinit区别于类构造器 init

        • 不需要调用父类构造器

        • 子类clinit执行前 父类的clinit一定会被执行

        • 虚拟机第一个执行的clinit是 java.lang.object

      • 静态语句块只可被赋值不能被访问

      • 接口与类不同 执行子接口的clinit并不需要执行负借口的clinit

对象的创建 内存分配 访问定位

对象的创建

                    A a =new A(); //当遇到关键字new指令时,Java对象创建过程便开始</pre>
类加载过程.png
加载过程
类加载检查
  1. 检查该new指令的参数 是否在常量池中定位到了一个类的符号引用 没有即创建对象失败

  2. 检查该类符号引用代表的类是否已经被加载,解析和初始化过

  3. 如果没有 需要先执行类的加载过程

为对象分配内存

对象所需要的内存大小在类加载完成后便可以完全确定

内存分配 根据java堆内存是否绝对规整分为

  1. 指针碰撞 Compat 收集器

    1. 假设java堆内存绝对规整 内存分配采用指针碰撞

    2. 分配形式: 已使用内存在一边 未使用的在另一边 中间放一个座位分界点的指示器

    3. 那么 分配对象内存 = 指针针向未使用内存一定一段与对象大小相等的距离

  2. 空闲列表 CMS 收集器

    1. 假设java堆内存不规整 内存分配将采用空闲列表

    2. 分配形式 :虚拟机维护着一个记录可用内存块的列表 在分配时从列表中找到一块足够大的空间划分给对象实例 ,并更新列表上的记录

内存创建在虚拟机中非常常见 存在并发情况下也会引起线程不安全

解决办法

  1. 同步处理分配内存空间 虚拟机采用CAS + 失败重试 保证更新操作的原子性

  2. 把内存分配行为按照线程划分在不同的内存空间进行

    1. 即每个线程在 Java堆中预先分配一小块内存(本地线程分配缓冲(Thread Local Allocation BufferTLAB)),哪个线程要分配内存,就在哪个线程的TLAB上分配,只有TLAB用完并分配新的TLAB时才需要同步锁。

    2. 虚拟机是否使用TLAB,可以通过-XX:+/-UseTLAB参数来设定。

将内存空间初始化为0

内存分配完成后 虚拟机需要将分配的内存空间初始化为0

  1. 保证对象的实例字段在使用时不可复制就能直接适应 (默认为0)
  1. 如果使用本线程分配缓冲(TLAB) 这一工作过程可以提前至TLAB分配时进行
对对象进行必要的设置

设置对象是哪个类的实例 如何找到类的源数据 对象的哈希吗 对象的GC分代信息等

这个信息存放在对象的对象头中

对象的内存分配

在java对象创建后 打底是如何被存储在java内存中的呢

在java虚拟机中 对象内存中 存储布局可以分为三块

1. 对象头
1.1 对象自身的运行时数据 ( Mark Word )
  1. 如Hash 码 GC分代 锁状态 线程持有的锁 偏向线程 id 偏向时间戳

  2. 该部分数据设计成1个非固定的数据结构 一边在绩效的空间存储更多信息

1.2 对象类型指针
  1. 对象指向它的类元数据的指针

  2. 虚拟机通过这个指针确定这个对象是哪个类的实例

1.3 数组的对象头

如果是对象是数组 name在对象头中还有一块记录数组长度的数据

因为虚拟机可以通过普通java 对象的元数据确定对象的大小 但是从数组的元数据中无法确定数组的大小

2. 实例数据

存储的信息 对象真正有效的信息

代码中定义的字段内容

  • 这部分数据的存储顺序会受到虚拟机分配参数(FieldAllocationStyle)和字段在Java源码中定义顺序的影响。
3. 对齐填充 (非必须)

存储 占位符

占位作用

因为对象的大小必须是8字节的整数倍(即对象的大小不是8字节的整数倍),就需要通过对齐填充来补全。

对象的访问定位

对象建立后 如何去访问对象?

实际上 需要访问的是 对象类型数据 和 对象实例数据

java 程序 通过 栈上的引用类型(reference) 来访问java栈上的对象

  • 句柄访问

  • 直接指针访问

JVM 分派

分派: 确定执行那个方法的过程

静态分派

1.1 定义

根据变量的静态类型进行方法分派的行为 根据变量的静态类型 确定执行那个方法 发生在编译器 不由JVM来执行

public class Test { 
    // 类定义
    static abstract class Human { 
    } 
    // 继承自抽象类Human
    static class Man extends Human { 
    } 
    static class Woman extends Human { 
    } 
    // 可供重载的方法
    public void sayHello(Human guy) { 
        System.out.println("hello,Human!"); 
    } 
 
    public void sayHello(Man guy) { 
        System.out.println("hello Man!"); 
    } 
 
    public void sayHello(Woman guy) { 
        System.out.println("hello Woman!"); 
    } 

// 测试代码
    public static void main(String[] args) { 
        Human man = new Man(); 
        Human woman = new Woman(); 
        Test test = new Test(); 

        test.sayHello(man); 
        test.sayHello(woman); 
    } 
}

// 运行结果
hello,Human! 
hello,Human!
1.2 方法重载

重载=静态分派 =根据变量的静态类型确定执行那个重载方法

1.3 变量的静态类型发生变化

强制装换类型 改变变量的静态类型

    Human man = new Man(); 
    test.sayHello((Man)man); 
1.4 静态分配的优先级匹配
  • 静态分派优先选择参数类型一致的重载方法

  • 没有最合适的方式 进行重载时 会选择(第二优先级)的方法重载 基本数据类型优先级分配

第二优先级顺序 : char > int > long > float > double > character > serializable> object .. args..

最后的args 为可变长参数 可以理解为数组

因为char转为byte 或者short过程不安全 所以不会选择参数类型为byte或者short进行重载

  • 引用类型分配 根据继承关系进行优先级匹配

动态分派

2.1 定义

根据变量的动态类型确定执行那个方法

// 定义类
    class Human { 
        public void sayHello(){ 
            System.out.println("Human say hello"); 
 
        } 
    } 
 
// 继承自 抽象类Human 并 重写sayHello()
    class Man extends Human { 
        @Override 
        protected void sayHello() { 
            System.out.println("man say hello"); 
 
        } 
    } 
 
    class Woman extends Human { 
        @Override 
        protected void sayHello() { 
            System.out.println("woman say hello"); 
 
        } 
    } 

// 测试代码
    public static void main(String[] args) { 

        // 情况1
        Human man = new man(); 
        man.sayHello(); 

        // 情况2
        man = new Woman(); 
        man.sayHello(); 
    } 
}

// 运行结果
man say hello
woman say hello

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