精华内容
下载资源
问答
  • 你有没有想过,你写的程序,是如何在计算机中运行的吗?比如我们搞Java的,肯定写过这段代码 public class HelloWorld { public static void main(String[] args) { System.out.println("Hello World!"); } ...

    强烈声明:本文很干,请自备茶水!😎

    开门见山,咱不说废话!

    你有没有想过,你写的程序,是如何在计算机中运行的吗?比如我们搞Java的,肯定写过这段代码

    public class HelloWorld {
        public static void main(String[] args) {
            System.out.println("Hello World!"); 
        }
    }
    

    熟悉吧,可能大家都知道,运行输出“Hello World!”,不过嘞,今天我带你从内存和CPU的角度去剖析一下这段代码在计算机中是如何执行的。

    Hello World保存在哪?

    首先问你个很简单的问题,当你写下这段代码的时候,你把它保存在哪了,假如说你说是桌面,就像现在这样

    是的,一个HelloWorld.java文件就保存在了桌面上,这个叫做Java源文件,不过说是保存在桌面上这个有点模糊,这个桌面又是个啥呢,我们应该想象它保存在了我们电脑的哪个部分呢?一台计算机包含如下基本五个部分:

    1. 运算器
    2. 控制器
    3. 存储器
    4. 输入设备
    5. 输出设备

    在计算机中啊,保存信息主要靠存储器,而存储器又分为内部存储器和外部存储器,内部存储器就是内存了,而这个外部存储器主要就是磁盘了,磁盘又分为硬盘和软盘,而我们电脑中的大部分就都是硬盘了,这里也分为固态硬盘和传统机械硬盘。

    不管怎么说吧,磁盘也好内存也罢,就是用来存放数据的,那么我们就知道了我们写的这个Java源文件应该就是存放在电脑中的磁盘了。

    那么代码保存到硬盘中之后呢?

    今天的主角,CPU和内存

    代码被保存到硬盘之后,接下来就要CPU和内存登场了,提起精神了,超级干货来了。

    告诉你个秘密,理解CPU和内存是如何在一块工作的,是每个学编程必备的基础中的基础,反正就是很重要!

    我们在上面提到了硬盘,这里又说到了CPU和内存,那么我们就来看看这三个家伙

    硬盘,内存和CPU

    先看一张图

    这张图大致描述了三者的一个关系,什么关系嘞,数据读取的关系,我们之前不是说我们写的代码保存到了硬盘中了吗,如果要执行这段代码的话,需要把这段代码从硬盘中读取到内存中,此时的代码已经变成了二进制文件存储在内存中,为了更好的去理解,我们把上面的代码再进一步改写下:

    public class HelloWorld {
        public static void main(String[] args) {
            int a = 5;
            int b = 3;
            sum = a + b;
            System.out.println(sum); 
        }
    }
    

    也就是加入了一个基础的加法运算,此时这段代码已经变成二进制文件放到了内存中,现在要继续执行,也就是要执行代码的加法操作,此时的数据a和b,也就是5和3已经是二进制的形式在内存中,现在需要把a和b做加法运算,CPU收到这个指令(将a和b相加),就会去内存中将a和b读取,然后存放到自己的寄存器中,然后由CPU中的运算器去做加法运算,得到的结果依然放到寄存器中。

    是不是有点懵?啥是寄存器?运算器呢?别急,我们继续往下看。

    内存是个啥?

    这个内存是啥呢?内存条知道吧,我们电脑中的内存条,它是属于内部存储器,主要就是用来存放数据的,也可以理解为我们这里说的内存,这是实际存在的,我们可以看得见的内存条,但是关于数据存储,怎么存储,是不是还有点模糊,那么我们抽象点来说,这个内存其实就是一个个的小格子,就像这样:

    这些小格子就是用来存放数据和指令的,而且每个格子都有自己的编号,这个编号大家也熟悉,就是我们经常说的内存地址。

    另外你需要知道的是,计算机是只认识0和1的,所以存放在内存中的数据其实都是二进制文件了。

    CPU是个重点

    其实关于内存,先了解上面那些就足够了,内容比较多的就是CPU了,想必大家都听说过CPU就相当于人的大脑一样,对电脑来说,至关重要,所以啊,CPU的结构是真的复杂,怎么办?

    一般碰到这样的,我们就需要有选择的进行掌握和学习,因此对于CPU,你必须了解的两个东西,一是寄存器,另外一个就是运算器,为啥,这又是啥?

    想要搞明白这个问题,咱们需要先来看看如下这些知识点。

    CPU的特点

    首先,这家伙是真的复杂,除此之外嘞,你还需要知道对于CPU来说,必须要把数据读取到寄存器中才能做相应的运算,另外CPU的数据读写的速度是真的快,内存和硬盘根本没法比,可能你没啥概念,举个例子吧

    CPU这家伙执行一条指令大约需要耗时1ns,但是对于内存呢?它去硬盘读取数据至少8ms,在这段时间内,CPU可以执行大约800万条指令,说这个,主要是为了让你直观的感受到CPU是真的快。

    就像《码农翻身》一书中说的那样:

    CPU的运算速度快的丧心病狂,但是它能做的事情简单的令人发指

    啥?为啥做的事情简单的令人发指呢?

    别看CPU那么牛,那么复杂,那么快,它做的事情真的挺无聊的,总结起来,CPU主要就干如下四件事情:

    1、从内存中读取数据,然后放到寄存器中

    2、把寄存器中的数据写入到内存

    3、进行数学运算和逻辑运算(加减乘除,AND,OR)

    4、依据相应的条件进行跳转,执行其他指令(一条指令跳转到另外一条指令)

    不知道你发现没,我们似乎一直在提寄存器,这又是什么玩意?这就要看看CPU的构造了

    CPU的重点组成部分

    我们上面也说了,CPU的结构真的很复杂,但是我们需要重点关注如下几个重要组成部分:

    1. 寄存器
    2. 运算器

    没错就是这俩货,那他们是干啥的嘞,首先你得知道他们是CPU中的东西,这个寄存器啊,说白了就是存放数据的,是个存储部件,还记得之前说内存就是一个个的小格子吗?这个寄存器你就可以简单的想成也是小格子,放数据的。

    寄存器

    寄存器是存储部件,容量非常有限,能存储的数据的大小,现在来说一般有两种,也就是我们常听说的32位和64位,32位的寄存器就能存储4个字节的数据,64位的寄存器就能存储8个字节的数据,另外CPU也分为32位和64位其实就是由其中的寄存器的位数决定的,可想而知,即使是64位的寄存器也干不了啥大事啊,所以啊,现在的CPU一般都内置了很多的寄存器,依此来完成比较复杂的操作

    运算器

    另外一个就是运算器了,这个是计算的核心,主要的作用就是用来做加减乘除这些运算的,不过嘞,这里你需要知道的一点就是,运算器是没法直接操作内存中的数据的,很容易想到,运算器操作的数据是寄存器中存放的数据。

    CPU和内存的交互

    简单的了解了内存和CPU之后,我们就可以来看看程序代码是如何进一步被执行的,上面提到了,程序代码被读取到内存中了,现在要执行这段代码,那就需要CPU出马了,首先嘞,CPU会去读取需要进行操作的数据,对了,继续看这代码:

    public class HelloWorld {
        public static void main(String[] args) {
            int a = 5;
            int b = 3;
            sum = a + b;
            System.out.println(sum); 
        }
    }
    

    也就是需要读取数据a和b,那么读取到的数据a和b存放在哪了呢?根据我们上面对CPU简单的了解可知,数据a和b是被放到了CPU中的寄存器中了,看这个图

    在CPU中有两个比较重要的组件就是运算器和寄存器,寄存器有很多个,再看内存,是一个个的小格子,每个格子有编号,比如说现在数据a和b分别存放在#1和#2上,然后CPU将他们读取放在寄存器R1和R2上

    这个时候就要计算a和b的和了,然后就轮到运算器出马了,它会拿到寄存器R1和R2,也就是拿到数据a和b,然后做加法运算

    那么计算之后的结果也就是sum,运算器会再次把它放到寄存器,比如R1中,这个时候之前的数据b就会被覆盖,如此一来,CPU和内存就一起完成一次加法运算操作。

    这里其实我们把关注点聚焦到了加法运算操作,实际上,程序代码被装载近内存的时候会产生数据和指令两部分,数据我们都知道是啥,指令嘞?

    其实也好理解,指令就是说明程序该怎么执行,对于CPU来说就是告诉CPU该做什么,比如告诉CPU,读取数据a和b,然后再将他们相加,说白了,CPU是根据指令干活,指令让怎么干,咱就怎么干。

    因此,你必须告诉CPU该干啥,否则,CPU也很懵的呦。

    经过上面的简单介绍,我们再来看看CPU主要做的四件事情:

    1、从内存中读取数据,然后放到寄存器中

    2、把寄存器中的数据写入到内存

    3、进行数学运算和逻辑运算(加减乘除,AND,OR)

    4、依据相应的条件进行跳转,执行其他指令(一条指令跳转到另外一条指令)

    怎么样,是不是更加清晰明了!

    总结一哈

    这么一看,也没啥难的吗?总的来说啊,就是我们写的程序是放在硬盘中的,在运行的时候才会被调入到内存中,也就是说内存中的数据是从硬盘来的,而CPU中寄存器的数据又是从内存中装载进来的,然后CPU会根据相应的指令去操作寄存器中的数据,比如加减乘除什么的,以此来完成一个程序在计算机中的运行。

    一句话总结:

    本身程序是在硬盘上,需要把程序加载进内存,然后由CPU去执行

    另外对于内存啊,CPU什么呢还有很多有趣有用的知识,我们下次再讲!

    感谢阅读

    大学的时候选择了自学Java,工作了发现吃了计算机基础不好的亏,学历不行这是没办法的事,只能后天弥补,于是在编码之外开启了自己的逆袭之路,不断的学习Java核心知识,深入的研习计算机基础知识,所有心得全部书写成文,整理成有目录的PDF,持续原创,PDF在公众号持续更新,如果你也不甘平庸,那就与我一起在编码之外,不断成长吧!

    其实这里不仅有技术,更有那些技术之外的东西,比如,如何做一个精致的程序员,而不是“屌丝”,程序员本身就是高贵的一种存在啊,难道不是吗?

    非常欢迎你的加入,未来的日子,编码之外,有你有我,一起做一个人不傻,钱很多,活得久的快乐的程序员吧!

    回复关键字“PDF”,获取技术文章合集,已整理好,带有目录,欢迎一起交流技术!

    另外回复“庆哥”,看庆哥给你准备的惊喜大礼包,只给首次关注的你哦!

    任何问题,可以加庆哥微信:H653836923,另外,我有个交流群,我会***不定期在群里分享学习资源,不定时福利***,感兴趣的可以说下我邀请你!

    对了,如果你是个Java小白的话,也可以加我微信,我相信你在学习的过程中一定遇到不少问题,或许我可以帮助你,毕竟我也是过来人了!

    在这里插入图片描述

    感谢各位大大的阅读🥰

    展开全文
  • JVM是如何运行Java代码

    千次阅读 2018-12-07 19:50:29
    作为一名 Java 程序员,你应该知道,Java 代码有很多种不同的运行方式。比如说可以在开发工具中运行,可以双击执行 jar 文件运行,也可以在命令行中运行,甚至可以在网页中运行。当然,这些执行方式都离不开 JRE,也...

    背景知识

     

    作为一名 Java 程序员,你应该知道,Java 代码有很多种不同的运行方式。比如说可以在开发工具中运行,可以双击执行 jar 文件运行,也可以在命令行中运行,甚至可以在网页中运行。当然,这些执行方式都离不开 JRE,也就是 Java 运行时环境。实际上,JRE 仅包含运行 Java 程序的必需组件,包括 Java 虚拟机以及 Java 核心类库等。我们 Java 程序员经常接触到的 JDK(Java 开发工具包)同样包含了 JRE,并且还附带了一系列开发、诊断工具。然而,运行 C++ 代码则无需额外的运行时。我们往往把这些代码直接编译成 CPU 所能理解的代码格式,也就是机器码。

    既然 C++ 的运行方式如此成熟,那么你有没有想过,为什么 Java 要在虚拟机中运行呢,Java 虚拟机具体又是怎样运行 Java 代码的呢,它的运行效率又如何呢?

    为什么Java要在虚拟机里运行?

    Java 作为一门高级程序语言,它的语法非常复杂,抽象程度也很高。因此,直接在硬件上运行这种复杂的程序并不现实。所以呢,在运行 Java 程序之前,我们需要对其进行一番转换。

    这个转换具体是怎么操作的呢?当前的主流思路是这样子的,设计一个面向 Java 语言特性的虚拟机,并通过编译器将 Java 程序转换成该虚拟机所能识别的指令序列,也称 Java 字节码。这里顺便说一句,之所以这么取名,是因为 Java 字节码指令的操作码(opcode)被固定为一个字节。

    举例来说,下图的中间列,正是用 Java 写的 Helloworld 程序编译而成的字节码。可以看到,它与 C 版本的编译结果一样,都是由一个个字节组成的。

    并且,我们同样可以将其反汇编为人类可读的代码格式(如下图的最右列所示)。不同的是,Java 版本的编译结果相对精简一些。这是因为 Java 虚拟机相对于物理机而言,抽象程度更高。

    # 最左列是偏移;中间列是给虚拟机读的机器码;最右列是给人读的代码

    0x00; b2 00 02; getstatic java.lang.System.out

    0x03; 12 03; ldc "Hello, World!"

    0x05; b6 00 04; invokevirtual java.io.PrintStream.println

    0x08; b1; return

     

    Java 虚拟机可以由硬件实现,但更为常见的是在各个现有平台(如 Windows、Linux)上提供软件实现。这么做的意义在于,一旦一个程序被转换成 Java 字节码,那么它便可以在不同平台上的虚拟机实现里运行。这也就是我们经常说的“一次编写,到处运行”。

    虚拟机的另外一个好处是它带来了一个托管环境(Managed Runtime)。这个托管环境能够代替我们处理一些代码中冗长而且容易出错的部分。其中最广为人知的当属自动内存管理与垃圾回收,这部分内容甚至催生了一波垃圾回收调优的业务。

    除此之外,托管环境还提供了诸如数组越界、动态类型、安全权限等等的动态检测,使我们免于书写这些无关业务逻辑的代码。

    Java虚拟机具体是怎样运行Java字节码的?

    下面以标准 JDK 中的 HotSpot 虚拟机为例,从虚拟机以及底层硬件两个角度,大概讲一讲 Java 虚拟机具体是怎么运行 Java 字节码的。

    从虚拟机视角来看,执行 Java 代码首先需要将它编译而成的 class 文件加载到 Java 虚拟机中。加载后的 Java 类会被存放于方法区(Method Area)中。实际运行时,虚拟机会执行方法区内的代码。

    Java 虚拟机在内存中划分出堆和栈来存储运行时数据,虚拟机会将栈细分为面向 Java 方法的 Java 方法栈,面向本地方法(用 C++ 写的 native 方法)的本地方法栈,以及存放各个线程执行位置的 PC 寄存器。

    如果有正在学java的程序员,可来我们的java技术学习扣qun哦:72340,3928,小编花了近一个月整理了一份非常适合18年学习的java干货,加入就免费送java的视频教程噢!而且我每天晚上都会在里面直播讲Java知识,从零基础学习到有基础进阶,欢迎初学和进阶中的小伙伴。

    在运行过程中,每当调用进入一个 Java 方法,Java 虚拟机会在当前线程的 Java 方法栈中生成一个栈帧,用以存放局部变量以及字节码的操作数。这个栈帧的大小是提前计算好的,而且 Java 虚拟机不要求栈帧在内存空间里连续分布。

    当退出当前执行的方法时,不管是正常返回还是异常返回,Java 虚拟机均会弹出当前线程的当前栈帧,并将之舍弃。

    从硬件视角来看,Java 字节码无法直接执行。因此,Java 虚拟机需要将字节码翻译成机器码。

    在 HotSpot 里面,上述翻译过程有两种形式:第一种是解释执行,即逐条将字节码翻译成机器码并执行;第二种是即时编译(Just-In-Time compilation,JIT),即将一个方法中包含的所有字节码编译成机器码后再执行。

     

     

    前者的优势在于无需等待编译,而后者的优势在于实际运行速度更快。HotSpot 默认采用混合模式,综合了解释执行和即时编译两者的优点。它会先解释执行字节码,而后将其中反复执行的热点代码,以方法为单位进行即时编译。

    Java虚拟机的运行效率究竟是怎样的?

    HotSpot 采用了多种技术来提升启动性能以及峰值性能,刚刚提到的即时编译便是其中最重要的技术之一。

    即时编译建立在程序符合二八定律的假设上,也就是百分之二十的代码占据了百分之八十的计算资源。

    对于占据大部分的不常用的代码,我们无需耗费时间将其编译成机器码,而是采取解释执行的方式运行;另一方面,对于仅占据小部分的热点代码,我们则可以将其编译成机器码,以达到理想的运行速度。

    理论上讲,即时编译后的 Java 程序的执行效率,是可能超过 C++ 程序的。这是因为与静态编译相比,即时编译拥有程序的运行时信息,并且能够根据这个信息做出相应的优化。

    举个例子,我们知道虚方法是用来实现面向对象语言多态性的。对于一个虚方法调用,尽管它有很多个目标方法,但在实际运行过程中它可能只调用其中的一个。

    这个信息便可以被即时编译器所利用,来规避虚方法调用的开销,从而达到比静态编译的 C++ 程序更高的性能。

    为了满足不同用户场景的需要,HotSpot 内置了多个即时编译器:C1、C2 和 Graal。Graal 是 Java 10 正式引入的实验性即时编译器,之所以引入多个即时编译器,是为了在编译时间和生成代码的执行效率之间进行取舍。C1 又叫做 Client 编译器,面向的是对启动性能有要求的客户端 GUI 程序,采用的优化手段相对简单,因此编译时间较短。

    C2 又叫做 Server 编译器,面向的是对峰值性能有要求的服务器端程序,采用的优化手段相对复杂,因此编译时间较长,但同时生成代码的执行效率较高。

    从 Java 7 开始,HotSpot 默认采用分层编译的方式:热点方法首先会被 C1 编译,而后热点方法中的热点会进一步被 C2 编译。

    为了不干扰应用的正常运行,HotSpot 的即时编译是放在额外的编译线程中进行的。HotSpot 会根据 CPU 的数量设置编译线程的数目,并且按 1:2 的比例配置给 C1 及 C2 编译器。

    在计算资源充足的情况下,字节码的解释执行和即时编译可同时进行。编译完成后的机器码会在下次调用该方法时启用,以替换原本的解释执行。

    总结

    • Java代码之所以要在虚拟机中运行,是因为它提供了可移植性。一旦 Java 代码被编译为 Java 字节码,便可以在不同平台上的 Java 虚拟机实现上运行。此外,虚拟机还提供了一个代码托管的环境,代替我们处理部分冗长而且容易出错的事务,例如内存管理。
    • Java 虚拟机将运行时内存区域划分为五个部分,分别为方法区、堆、PC 寄存器、Java 方法栈和本地方法栈。Java 程序编译而成的 class 文件,需要先加载至方法区中,方能在 Java 虚拟机中运行。
    • 为了提高运行效率,标准 JDK 中的 HotSpot 虚拟机采用的是一种混合执行的策略。
    • 它会解释执行 Java 字节码,然后会将其中反复执行的热点代码,以方法为单位进行即时编译,翻译成机器码后直接运行在底层硬件之上。
    • HotSpot 装载了多个不同的即时编译器,以便在编译时间和生成代码的执行效率之间做取舍。
    展开全文
  • 他们把用户提交上来的代码保存到一个文件里面, 如果 c 语言就要先进行编译,然后再执行可执行文件,然后再发执行结果返加到用户看到的网页上面, 比如http://c.jsrun.net 就是c语言的在线代码运行。 其中的过程...

    在线运行代码, 比如http://jsrun.net就是支持非常多种语言的支持。

    他们是把用户提交上来的代码保存到一个文件里面, 如果是 c 语言就要先进行编译,然后再执行可执行文件,然后再发执行结果返加到用户看到的网页上面, 比如http://c.jsrun.net 就是c语言的在线代码运行。

    其中的过程其实非常复杂, 但是 JSRUN 的在线执行非常的快, 主要是用了 超高速 SSD 增加文件读写能力, 这种 SSD 一般需要3GB/s以上的读写速度才可以。

    另外还要解决的安全性问题, 用户如果提交上来的是死循环,那么直接机器就卡死了。 如果提交上来的代码是删除整个硬盘,那么服务器就完蛋了。 中间要作非常的多的识别 来排除恶意代码的运行。

    JSRUN 是非常好的一个网站,在国内已经是大龙头了, 建议你直接使用他们的服务就可以了。 做这些工作需要大量的精力维护,不建议再重复造轮子。

    https://www.v2ex.com/t/659859

    展开全文
  • 关注、星标公众号,不错过精彩内容来源:21ic电子网整理:strongerHuang要把我们编写的一个C程序源代码转换成可以在硬件上运行的程序(可执行代码:hex/bin...
        

    关注、星标公众,不错过精彩内容

    640?wx_fmt=jpeg

    来源:21ic电子网
    整理:strongerHuang

    要把我们编写的一个C程序源代码转换成可以在硬件上运行的程序(可执行代码:hex/bin等),需要进行编译和链接。

    编译:把文本形式源代码翻译为机器语言形式的目标文件的过程。

    链接:把目标文件、操作系统的启动代码和用到的库文件进行组织形成最终生成可执行代码的过程。

    从下图可以看到,整个代码的编译过程分为编译和链接两个过程,编译对应图中的大括号括起的部分,其余则为链接过程。

    640?wx_fmt=png

    编译过程又可以分成两个阶段:编译和汇编

    一、编译

    编译是读取源程序(字符流),对之进行词法和语法的分析,将高级语言指令转换为功能等效的汇编代码,源文件的编译过程包含两个主要阶段:

    1.预处理阶段

    第一个阶段是预处理阶段,在正式的编译阶段之前进行。预处理阶段将根据已放置在文件中的预处理指令来修改源文件的内容。如#include指令就是一个预处理指令,它把头文件的内容添加到.cpp文件中。这个在编译之前修改源文件的方式提供了很大的灵活性,以适应不同的计算机和操作系统环境的限制。

    一个环境需要的代码跟另一个环境所需的代码可能有所不同,因为可用的硬件或操作系统是不同的。在许多情况下,可以把用于不同环境的代码放在同一个文件中,再在预处理阶段修改代码,使之适应当前的环境。

    640?wx_fmt=jpeg

    主要是以下几方面的处理:

    a.宏定义指令

    如 #define a  b

    对于这种伪指令,预编译所要做的是将程序中的所有a用b替换,但作为字符串常量的 a则不被替换。还有 #undef,则将取消对某个宏的定义,使以后该串的出现不再被替换。

    b.条件编译指令

    如#ifdef,#ifndef,#else,#elif,#endif等

    这些伪指令的引入使得程序员可以通过定义不同的宏来决定编译程序对哪些代码进行处理。预编译程序将根据有关的文件,将那些不必要的代码过滤掉。

    c.头文件包含指令

    如#include "FileName"或者#include等

    在头文件中一般用伪指令#define定义了大量的宏(最常见的是字符常量),同时包含有各种外部符号的声明。采用头文件的目的主要是为了使某些定义可以供多个不同的C源程序使用。因为在需要用到这些定义的C源程序中,只需加上一条#include语句即可,而不必再在此文件中将这些定义重复一遍。

    预编译程序将把头文件中的定义统统都加入到它所产生的输出文件中,以供编译程序对之进行处理。包含到c源程序中的头文件可以是系统提供的,这些头文件一般被放在 /usr/include目录下。

    在程序中#include它们要使用尖括号(< >)。另外开发人员也可以定义自己的头文件,这些文件一般与c源程序放在同一目录下,此时在#include中要用双引号("")。

    d.特殊符号

    预编译程序可以识别一些特殊的符号

    例如在源程序中出现的LINE标识将被解释为当前行号(十进制数),FILE则被解释为当前被编译的C源程序的名称。预编译程序对于在源程序中出现的这些串将用合适的值进行替换。

    预编译程序所完成的基本上是对源程序的“替代”工作。经过此种替代,生成一个没有宏定义、没有条件编译指令、没有特殊符号的输出文件。这个文件的含义同没有经过预处理的源文件是相同的,但内容有所不同。下一步,此输出文件将作为编译程序的输出而被翻译成为机器指令。

    2.编译优化阶段

    第二个阶段编译、优化阶段,经过预编译得到的输出文件中,只有常量;如数字、字符串、变量的定义,以及c语言的关键字,如main,if,else,for,while,{,}, +,-,*,等等。

    编译程序所要作得工作就是通过词法分析和语法分析,在确认所有的指令都符合语法规则之后,将其翻译成等价的中间代码表示或汇编代码。

    优化处理是编译系统中一项比较艰深的技术。它涉及到的问题不仅同编译技术本身有关,而且同机器的硬件环境也有很大的关系。优化一部分是对中间代码的优化。这种优化不依赖于具体的计算机。另一种优化则主要针对目标代码的生成而进行的。

    640?wx_fmt=jpeg

    对于前一种优化,主要的工作是删除公共表达式、循环优化(代码外提、强度削弱、变换循环控制条件、已知量的合并等)、复写传播,以及无用赋值的删除,等等。

    后一种类型的优化同机器的硬件结构密切相关,最主要的是考虑是如何充分利用机器的各个硬件寄存器存放的有关变量的值,以减少对于内存的访问次数。另外,如何根据机器硬件执行指令的特点(如流水线、RISC、CISC、VLIW等)而对指令进行一些调整使目标代码比较短,执行的效率比较高,也是一个重要的研究课题。

    二、汇编

    汇编实际上指把汇编语言代码翻译成目标机器指令的过程。对于被翻译系统处理的每一个C语言源程序,都将最终经过这一处理而得到相应的目标文件。目标文件中所存放的也就是与源程序等效的目标的机器语言代码。目标文件由段组成。通常一个目标文件中至少有两个段:

    代码段:该段中所包含的主要是程序的指令。该段一般是可读和可执行的,但一般却不可写。

    数据段:主要存放程序中要用到的各种全局变量或静态的数据。一般数据段都是可读,可写,可执行的。

    1.UNIX环境下的三种目标文件

    可重定位文件:其中包含有适合于其它目标文件链接来创建一个可执行的或者共享的目标文件的代码和数据。

    共享的目标文件:这种文件存放了适合于在两种上下文里链接的代码和数据。第一种是链接程序可把它与其它可重定位文件及共享的目标文件一起处理来创建另一个 目标文件;第二种是动态链接程序将它与另一个可执行文件及其它的共享目标文件结合到一起,创建一个进程映象。

    可执行文件:它包含了一个可以被操作系统创建一个进程来执行之的文件。汇编程序生成的实际上是第一种类型的目标文件。对于后两种还需要其他的一些处理方能得到,这个就是链接程序的工作了。

    2.链接过程

    由汇编程序生成的目标文件并不能立即就被执行,其中可能还有许多没有解决的问题。

    例如,某个源文件中的函数可能引用了另一个源文件中定义的某个符号(如变量或者函数调用等);在程序中可能调用了某个库文件中的函数,等等。所有的这些问题,都需要经链接程序的处理方能得以解决。

    链接程序的主要工作就是将有关的目标文件彼此相连接,也即将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够诶操作系统装入执行的统一整体。

    根据开发人员指定的同库函数的链接方式的不同,链接处理可分为两种:

    静态链接:在这种链接方式下,函数的代码将从其所在地静态链接库中被拷贝到最终的可执行程序中。这样该程序在被执行时这些代码将被装入到该进程的虚拟地址空间中。静态链接库实际上是一个目标文件的集合,其中的每个文件含有库中的一个或者一组相关函数的代码。

    动态链接:在此种方式下,函数的代码被放到称作是动态链接库或共享对象的某个目标文件中。链接程序此时所作的只是在最终的可执行程序中记录下共享对象的名字以及其它少量的登记信息。在此可执行文件被执行时,动态链接库的全部内容将被映射到运行时相应进程的虚地址空间。动态链接程序将根据可执行程序中记录的信息找到相应的函数代码。

    对于可执行文件中的函数调用,可分别采用动态链接或静态链接的方法。使用动态链接能够使最终的可执行文件比较短小,并且当共享对象被多个进程使用时能节约一些内存,因为在内存中只需要保存一份此共享对象的代码。但并不是使用动态链接就一定比使用静态链接要优越。在某些情况下动态链接可能带来一些性能上损害。

    我们在Linux使用的gcc编译器便是把以上的几个过程进行捆绑,使用户只使用一次命令就把编译工作完成,这的确方便了编译工作,但对于初学者了解编译过程就很不利了,下图便是gcc代理的编译过程:

    640?wx_fmt=jpeg

    从上图可以看到:

    预编译:

    将.c 文件转化成 .i文件

    使用的gcc命令是:gcc –E

    对应于预处理命令:cpp

    编译

    将.c/.h文件转换成.s文件

    使用的gcc命令是:gcc –S

    对应于编译命令是:cc –S

    汇编

    将.s 文件转化成 .o文件

    使用的gcc 命令是:gcc –c

    对应于汇编命令是:as

    链接

    将.o文件转化成可执行程序

    使用的gcc 命令是:gcc

    对应于链接命令是ld


    三、结语
    总结起来编译过程就上面的四个过程:预编译、编译、汇编、链接

    了解这四个过程中所做的工作,对我们理解头文件、库等的工作过程是有帮助的,而且清楚的了解编译链接过程还对我们在编程时定位错误,以及编程时尽量调动编译器的检测错误会有很大的帮助的。

      END  



    推荐阅读:

    关注公众号『strongerHuang』,在后台回复“1024”,查看加群”,可加入

    640?wx_fmt=jpeg

    长按识别图中二维码关注

    展开全文
  • Java代码是如何在虚拟机中运行的

    千次阅读 2018-08-01 10:29:19
    想像C语言一样直接运行在硬件上显然不可能的,所以在Java程序运行之前,需要将Java程序通过编译器转换成虚拟机所能识别的Java字节码,Java字节码都固定的一个字节,因此只要将Java程序转换成虚拟机所能识别的...
  • Java代码是怎么运行的

    千次阅读 2018-09-02 22:21:33
    Java代码是怎么运行的? 版本: 2018/9/2-1 Java代码是怎么运行的? JRE JDK C++ Java虚拟机 Java字节码 实现方式 托管环境 Java虚拟机如何运行Java字节码? 虚拟机角度 硬件角度 思考题 参考资料 1...
  • 如何运行GitHub上的代码

    千次阅读 2019-10-09 16:04:22
    如何从GitHub上下载自己需要的项目: 1.首先,你要有一个自己的GitHub(https://github.com/)的账号。关于如何注册GitHub的账号以及如何获取你的SSH密钥这个问题,请大家参考...
  • 如何运行Faster RCNN的tensorflow代码

    千次阅读 2018-08-07 13:56:17
    0.目的  刚刚学习faster rcnn目标检测算法,在尝试跑通github上面Xinlei Chen的tensorflow版本的faster rcnn代码时候遇到很多问题(我真是太菜),... 代码的README里面说明了,环境要求既有这个git里面的,...
  • MAC上如何运行C++代码

    万次阅读 2018-08-08 00:27:11
    C++当前比较流行的开发工具位dev c++,但是在MAC上没有dev c++的安装软件,因此我们需要使用Xcode来进行运行C++代码,具体的执行如下: 当我们写好代码之后,我们只需要点击左上角的运行按钮,则程序就输出运行...
  • 本文就来为大家详细讲讲如何运行Python代码。一般来讲,运行Python代码的方式有两种,一在Python交互式命令行下运行;另一种使用文本编辑器,在命令行中直接运行。这两种方法各有优缺点,下面我们以hello world...
  • 我们都知道要运行Java代码就必须要有JRE,也就是Java运行时环境,JRE中包含了Java程序的必需组件,包括Java虚拟机以及Java核心类库,然而运行C++代码则不需要额外的运行时环境,...他在虚拟机中又是如何运行的?接着...
  • 中文网: ... 介绍: Flutter谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户... Flutter可以与现有的代码一起工作。在全世界,Flutter正在被越来越多的开发者和组织使用,并且Flutter完全免费...
  • 原文:JavaScript 是如何运行的? 作者:hengg Fundebug经授权转载,版权归原作者所有。 什么是JavaScript? 我们来确认一下JavaScript的定义:JavaScript 是一门解释型的动态语言。 解释型语言是相对于编译型语言...
  • 使用前端编译期(javac、eclipse)将.java源代码编译成.class字节码文件,运行时JVM加载.class字节码文件交给解释器解释成相应平台的机器码执行 早期的JVM就是以这种方式运行java程序,由解释器一行行的将字节码解释...
  • 如何代码文件运行TensorFlow的命令

    千次阅读 2017-09-05 20:15:35
    先随便找个地方new一个test.py文件,然后执行命令---chmod a+x test.py(文件名),然后在文件中输入命令,保存后执行./test.py命令就可以运行了。 下面我的代码和执行结果
  • 其实最好直接使用vim在字符界面里面直接编写,这才是正道,但是作初学者,其实直接在服务器上写代码是很不习惯的,所以我这边希望能写一篇帮助师弟师妹使用本地的ide编写代码,然后服务器的解释器运行代码。...
  • 小弟才疏学浅看的书籍不多 难道编译成对应服务器所架系统的程序? 比如服务器架在win2003 小弟要在本机上用vs把服务器响应代码编成 win32程序移动到服务端运行
  • 我们在计算机中写的.c文件,被称为C语言源代码,这种源代码机器不认识的,为了让机器认识C语言源代码,那么必须要经过这么几个过程(预处理、编译、汇编、链接、加载),让源代码变为机器语言的目标文件,才可以...
  • C语言程序代码神奇效果如何运行

    千次阅读 2015-12-03 16:42:03
    外行人看代码是很麻烦的事情,只能看到神奇的程序员biubiu几行代码就能搞出很神奇的效果。为什么程序员简单的几行代码就可以打印出我们想要的结果呢,下面这篇文章就介绍下C语言程序的运行过程。  C语言的...
  • 程序是如何运行的

    千次阅读 2018-11-22 12:33:18
    对于任何一个学习过C语言的来说,“HelloWorld”程序都不会陌生。因为它应该你打开新...但是你们知道短短的几行代码怎么让程序运行起来的么? // hello.c #include &lt;stdio.h&gt; int main(int a...
  • **现在项目中需要实现,与 [fenby网](http://www.fenby.com/index "") 中的“闪电编程”相同的功能,目前在线编辑器已经实现,但是不知道如何才能把编辑好的JAVA或C之类的代码编译后运行,并显示出结果,坐等高人...
  • Jupyter Notebook运行Python代码如何传参

    千次阅读 2020-06-14 14:25:44
    Jupyter Notebook运行Python代码如何传参利用sys.argv模拟命令行参数...原因在Jupyter Notebook中运行Python代码时,argv中有三个默认参数,分别: argv[0]: ‘路径\test.py’ (源代码文件名) argv[1]: ‘-f’ ar
  • ![图片说明](https://img-ask.csdn.net/upload/201509/21/1442821763_878069.jpg) 下载之后没有样式 排版也不对 我没有下载样式文件 我直接引入的cdn
  • C的代码是如何变成程序的

    千次阅读 2013-04-07 22:00:25
    C的代码是如何变成程序的  C语言是一门典型的编译语言,源代码文件需要编译成目标代码文件才能运行。可以认为程序文件就是编译好的目标代码文件。 以GCC的编译过程为例。GCC的翻译过程可以分成四个阶段:预处理器、...
  • 如何让编辑器运行你的代码

    千次阅读 2017-01-13 14:37:21
    Unity3D可以通过事件触发来执行你的编辑器代码,但是我们需要一些...触发函数里可以编写任何合法的代码,可以一个资源批处理程序,也可以弹出一个编辑器窗口。代码里可以访问到当前选中的内容(通过Selection类),
  • iPad该如何运行Python代码

    千次阅读 2019-12-27 22:16:02
    其中最让人头疼的,就是运行环境的安装。 实事求是地讲,参加工作坊之前,我已经做了认真准备。 例如集成环境,选用了对用户很友好的Anaconda。 代码在我的Macbook电脑上跑,没有问题。还拿到学生的Windows 7上跑,...
  • 如何在ubuntu中写一个简单的C语言代码并编译运行

    万次阅读 多人点赞 2018-10-31 11:10:01
    因为笔者也刚刚开始学习ubuntu所以不知道各个编译器之间的区别,笔者所用的gcc就简单介绍一下gcc的安装方法吧。 方法一: 打开控制台输入以下代码: sudo apt-get build-dep gcc; sudo apt-get build-dep gcc; ...
  • 如何用BabeLua运行Lua代码

    万次阅读 2015-12-19 16:05:39
    一:BabeLua简单介绍  BabeLua一款基于微软Visual...二:如何用BabeLua成功运行Lua代码 1.首先你得安装好VisualStudio,我使用的VS2015+Windows10系统环境,当然你也可以使用VS2012或者2013。 2.接下来下载Ba
  • 最近在使用eclipse遇到了问题,程序无论怎么运行之前修改前的程序结果,如下面所示: package test; public class test { public static void main(String[] args) { // TODO Auto-generated method stub...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 25,124
精华内容 10,049
关键字:

代码是如何运行的