Java Core(一)之从字节码理解HelloWorld

2021/01/21 Java_SE 共 1998 字,约 6 分钟

从Java基础入手,再次回顾Java的细节:认识Java和C、C++的区别;了解一个Java类是如何被加载的;认识一个对象的创建流程

Java、C、C++的区别

  1. 按照语言特点来分类

    • 面向过程:C
    • 面向对象:Java、C++
  2. 有一个很有意思的说法,Java是C++-,因为Java屏蔽了很多C++中繁琐的语法、容易造成编程错误的指针和手动释放内存

  3. 和操作系统的关系

    image

    • C、C++直接和操作系统交互,有指针的概念,并且需要自己手动释放内存
    • Java和JVM(Java Vitural Machine)打交道,通过JVM和操作系统交互,没有指针、释放内存的概念
    • Java的效率比C、C++低
    • Java因为是和JVM打交道,所以只要JVM可以运行的地方,Java就可以直接移植上去,而C、C++不行

从一个HelloWorld来理解Java的运行原理

public class Hello {
    public static void main(String[] args){
        Hello hello = new Hello();
    }
}
  1. 当我想要运行一个Java程序的时候,第一件事情是编译,即javac XXX

    • javac就是调用Java的编译器程序,把java文件编译成JVM能够识别的字节码文件,也就是.class文件,然后这个.class文件会被存放在系统的磁盘中
  2. 编译完成,得到字节码文件,然后我们会执行java命令,运行刚才编译得到的.class文件

    1. Java命令开始,会在操作系统中开辟一个空间,这个空间就是JVM的空间
    2. 类加载器将磁盘上的.class文件加载到JVM的方法区内存中
    3. JVM找到加载的主类,并且找到主类中的main方法开始执行

    image

  3. 类加载详细过程

    image

    • 加载:类加载器将class文件加载进入JVM
    • 验证:JVM对加载进来的class文件进行验证
      • 验证当前class的版本和JVM的版本是否兼容
      • class文件的文件格式验证
      • 元数据验证,主要是语法校验,例如类实现了某个接口,接口中的方法重写没有;子类继承父类,父类中的抽象方法有没有重写
      • 字节码验证,主要是语义校验,例如某个对象是int类型却被当作long类型使用
    • 准备:将类中的类成员变量全部初始化(final变量要注意,直接就会被初始化成初始值),如果一个成员变量是基础数据类型的话,那么这个成员变量会被初始化成0,如果一个成员是引用数据类型的话,那么这个成员变量会被初始化成null
    • 解析:将常量池中的符号引用变为直接引用
    • 初始化:将我们定义的static代码块按顺序组织成类构造方法创教对象
    • 使用:初始化完毕的对象投入使用
    • 卸载:对象生命周期结束,进入卸载
  4. 每一个类的加载都是按照上述流程进行的

  5. 程序执行完毕

    • 这些都只是一个大致,有许多不严谨的地方,更严谨的讨论将会在JVM学习中阐述

理解一个对象的创建流程 Hello hello = new Hello()

对象创建过程

Hello hello = new Hello();其实是有2部分组成,从“=”处将这个过程一分为二

  1. 在main()方法的栈空间处申请一个Hello的引用
  2. 在堆内存中开辟一个对象的空间,然后将Hello的有关信息,如实例变量,实例方法等从方法区内存中加载到堆内存中
  3. 将栈内存中的引用指向堆内存中的对象

image

对象的组成部分

对象一共有三个组成部分,分别是对象头、实例数据、对齐填充字节

  1. 对象头一共有三个部分,分别是:Mark Word、指向类的指针、数组长度(只有数组对象才有)

    • mark word

      其中包含了对象的锁信息,32位的JVM和64位的JVM中mark word的长度是不同的,32位JVM中是32位,64位JVM中是64位,下面根据图来讲解(图中描绘的是32位的JVM)

      image

      首先我们关注最后两位,最后两位的00、01、10、11代表了不同的锁状态

      其中01代表的是无锁和偏向锁状态,这两者的区分依靠前面一位的是否是偏向锁来区分

      00代表了轻量级锁(synchronized中的轻量级锁就是自旋锁)

      10代表重量级锁(OS锁)

      11代表GC标记

      如果一个对象没有被上锁,那么这个对象的对象头的mark word的前25bit记录的就是这个对象的HashCode

      如果一个对象被上了偏向锁,那么这个对象的mark word中前23bit就会记住这个给对象加了锁的线程的ID

      如果一个对象被上了自旋锁,那么这个对象的mark word中前30位就会指向栈内存中锁记录的指针

      如果一个对象被上了重量级锁,那么这个对象的mark word中前30位就会指向重量级锁的指针

    • 指向类的指针

      被创建的类中只会包含对象的非静态方法和非静态实例,如果调用这个类的静态方法或静态实例的话需要拿着这个指针去方法区内存的方法表中去查找静态方法以及静态实例

    • 数组长度,这个部分只有数组对象才有

  2. 实例数据

    • 就是我们看到的Java对象的属性和值(非静态的)
  3. 对齐填充字节

    • 因为Java中对象的长度必须是8bit的倍数,所以在最后面会有专门用于填充到8bit倍数的字节

文档信息

Search

    Table of Contents