Java源代码是如何编译,加载到内存中的?

这篇具有很好参考价值的文章主要介绍了Java源代码是如何编译,加载到内存中的?。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1.前言

相信许多开发同学看过《深入理解java虚拟机》,也阅读过java虚拟机规范,书籍和文档给人的感觉不够直观,本文从一个简单的例子来看看jvm是如何工作的吧。

本文所有操作均在mac上进行。

2.示例代码

示例代码采用最常见的双重检索单例模式:

package interview.desginpattern.singletion.doublecheck;

import java.io.Serializable;

public class Singleton implements Serializable {
    private static volatile Singleton instance = null;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }

        return instance;
    }
}

3.指令

经过编译后,我们得到class文件,然后用javap命令查看相关指令

javap -c Singleton:

public class interview.desginpattern.singletion.doublecheck.Singleton implements java.io.Serializable {
  public static interview.desginpattern.singletion.doublecheck.Singleton getInstance();
    Code:
       0: getstatic     #2                  // get static instance
       3: ifnonnull     37                  // instance is null,jump 37
       6: ldc           #3                  // push class Singleton to operand stack
       8: dup                               // Duplicate the top (class Singleton) operand stack value
       9: astore_0                          // Store reference(class Singleton) into local variable
      10: monitorenter                      // synchronized start
      11: getstatic     #2                  
      14: ifnonnull     27                  // instance is null,jump 27
      17: new           #3                  // new class interview/desginpattern/singletion/doublecheck/Singleton
      20: dup                               
      21: invokespecial #4                  // Method "<init>":()V
      24: putstatic     #2                  
      27: aload_0                           // Load reference (class Singleton) from local variable
      28: monitorexit                       // synchronized end
      29: goto          37
      32: astore_1
      33: aload_0
      34: monitorexit                       // synchronized end (exception)
      35: aload_1
      36: athrow                            // throw exception
      37: getstatic     #2                  
      40: areturn
    Exception table:
       from    to  target type
          11    29    32   any
          32    35    32   any

  static {};                                // static code
    Code:
       0: aconst_null                       // Push the null object reference onto the operand stack
       1: putstatic     #2                  // set static instance
       4: return
}

有以下几点需要说明:

  • operand stack 即操作数栈,区别于jvm虚拟机栈,是属于栈帧(frame)中的数据结构
  • local variable 即本地变量,也属于栈帧中的结构
  • synchronized 被解析成monitorenter 和 monitorexit两条指令,并且需要处理异常情形

我们知道一般来讲jvm 运行时数据包括,pc寄存器,stack(虚拟机栈),heap,method area, 运行时常量池,本地方法栈。stack 中的每一帧包括,operand stack, local variable , 和指向常量池的指针。

4.class文件

上一节中只是展示了java代码编译成class文件,包含了哪些指令,但是class文件包含的信息远远不止这些。

我们在IDEA 中使用插件 jclasslib bytecode viewer 查看 class文件具体包含哪些信息:

4.1.一般信息

Java源代码是如何编译,加载到内存中的?

这里省略了class 文件的魔法数标志,0xCAFEBABE

4.2.接口和字段

Java源代码是如何编译,加载到内存中的?

Java源代码是如何编译,加载到内存中的?

4.3.方法

Java源代码是如何编译,加载到内存中的?

  • <init> 方法对应Singletion的构造方法
  • <clinit> 方法对应Singletion.class的构造方法

我们还可以查看getInstatnce编译后的指令 (chapter 3):

Java源代码是如何编译,加载到内存中的?

4.4.常量池

上述数据结构(4.1, 4.2, 4.3)不会存字符串字面量,而是指向常量池的引用:

Java源代码是如何编译,加载到内存中的?

4.5.属性

Java源代码是如何编译,加载到内存中的?

5.加载

加载即根据class文件创建对象/接口.

有两种加载器,分别是bootstrap class loader,和user-defined class cloader,我们可以自定义加载器,比如从网络,或者从加密的class文件中加载对象。

任何一个class/inteface 被限定名 + 类加载器唯一确定,所以jvm实现者在并发的情况下需要确保此唯一性约束。

一般而言,加载流程如下

  1. 看该文件有没有被对应的加载器进行加载
  2. 验证该文件是不是class文件,major or minor version 是否被支持
  3. 检查父类是否被加载
  4. 检查接口是否被加载

6.链接(验证,准备,解析)

验证:确保class文件结构是否正确,是否打破jvm规范

准备:给class或者interface的静态属性设置默认值(区别于显式赋值)

解析:给 symbolic references 赋予确定的值(除了 invokedynamic,其他都可以唯一确定

7.初始化

初始化:调用class或者interface的<init>, <cinit> 方法 (4.3)

8.总结

本文概述了java代码是如何加载到jvm中的,jvm有各种不同的实现(我们最熟悉的hotspot虚拟机),其中细节可能不尽相同。加载到jvm中并不代表程序生命周期的结束,运行时的情况也值得关注。

9.参考

https://docs.oracle.com/javase/specs/jvms/se8/jvms8.pdf文章来源地址https://www.toymoban.com/news/detail-635338.html

到了这里,关于Java源代码是如何编译,加载到内存中的?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Python反编译exe获取封装源代码

    先把exe和pyinstxtractor放在一起 会得到一个文件夹 安装解析库 一共有两个,还有个struct 用sublime打开两个文件,看到一堆二进制,其中struct是多一行的,把struct的第一行复制到前一个文件中 uncompyle6 C:UserslenovoDesktop20223102412-李凌飞exemerge_ALL_ALL_V1.0.exe_extractedmerge_ALL_ALL_V1.0

    2024年01月19日
    浏览(57)
  • ffmpeg学习 源代码编译、英伟达硬件加速

    使用cpu进行软编解码时,cpu效率低并且占用高。使用硬件加速,能够明显降低CPU的占用,参看博客 ffmpeg学习(16)AVDevice使用。 这里以使用英伟达gpu进行h264编解码加速为例说明,其他平台类似。 在windows平台直接下载官方的预编译ffmpeg、lib开发包,都已经支持了英伟达显卡硬

    2023年04月09日
    浏览(42)
  • mysql源代码编译安装(可自拟版本)

    centOS7 mysql5.6.40为例 关闭防火墙的条件下 1.yum安装 2.rpm查询一下 3.创建mysql用户,用于授权目录 -s /sbin/nologin 不允许用户进行任何交互操作 -M 不要自动创建用户的 home 目录 mysql 为用户名 查看用户是否已经存在 ​在想要指定目录下载 这里我以/opt/mysql为例 1.下载源码 可以换别

    2024年02月14日
    浏览(46)
  • X86架构下交叉编译arm64源代码

    glibc在/lib(/lib32 /lib64)目录下的.so文件为libc.so.6 glibc是GNU发布的libc库,即c运行库。glibc是linux系统中最底层的api,几乎其它任何运行库都会依赖于glibc。glibc除了封装linux操作系统所提供的系统服务外,它本身也提供了许多其它一些必要功能服务的实现。由于 glibc 囊括了几乎所有

    2023年04月21日
    浏览(54)
  • Linux Red Hat 9.0使用源代码编译安装Nginx

    目录 前言 编译安装Nginx源代码 下载安装必须的依赖项(GCC编译器,GNU make工具,PCRE库和zlib库) 创建nginx组和用户账户 进入浏览器,拉取nginx源码(Nginx 1.20.2版本) 上传到Red Hat(此处上传在/home/zyz下) 移动源码到root用户下 解压 进入nginx-1.20.2目录下 检查平台安装环境 执行

    2024年03月10日
    浏览(56)
  • 银河麒麟V10 SP1 ARM编译QT源代码

    最近因为一些事情需要在银河麒麟ARM系统上[银河麒麟V10 SP1 ARM 2203 linux5.4.96-11]安装QT5.12.4版本,由于QT官方不提供ARM版的安装包,无奈只能自己手动编译源代码。 后续补充:实际上我要装的是Qt5.14.2 而不是 5.12.4,不过同样的环境下Qt5.14.2也编译成功了。 这是一篇个人笔记,对

    2024年02月05日
    浏览(70)
  • 如何选择源代码加密软件

    比较内容 安全容器(SDC沙盒) DLP 文档加密 云桌面 代表厂家 *信达 卖咖啡、赛门贴科 亿*通、IP噶德、*盾、*途 四杰、深*服 设计理念 以隔离容器加准入技术为基础,构建只进不出,要出需走审批的数据安全环境,环境内数据一视同仁,不区分文件格式,一律保护。 以内容识别

    2024年02月07日
    浏览(63)
  • java记事本源代码

    本文仿电脑自带记事本,实现的功能有新建、新窗口、打开、保存、另存为、退出、撤销、剪切、复制、粘贴、删除、查找、查找下一个、查找上一个、替换、转到、全选、时间/日期、自动换行、缩放(放大、缩小、恢复默认大小),未实现功能有页面设置、打印、字体、状

    2024年02月10日
    浏览(50)
  • Linux内核中的TCP/IP协议栈源代码分析

    目录 背景知识-Linux源码简介 TCP/IP协议栈相关问题 inet_init是如何被调用的?从start_kernel到inet_init调用路径 1.start_kernel(): 2.inet_init() : 3.fs_initcall() 跟踪分析TCP/IP协议栈如何将自己与上层套接口与下层数据链路层关联起来的? TCP的三次握手源代码跟踪分析,跟踪找出设置和发

    2024年02月02日
    浏览(69)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包