编译器优化

Java的消亡史

随声附和 提交于 2019-12-14 11:07:54
作为一名程序猿 ,我们每天都在写Code,但你真的了解它的生命周期么?今天就来简单聊下它的生命历程,说起一段Java Code,从出生到game over大体分这么几步:编译、类加载、运行、GC。 编译 Java语言的编译期其实是一段“不确定 ”的过程,因为可能是一个前端编译器把.java文件转变为.class文件的过程;也可能是指JVM的后端运行期编译器(JIT编译器)把字节码转变为机器码的过程;还可能是指使用静态提前编译器(AOT编译器)直接把.java文件编译成本地机器码的过程。但是在这里我们说的是第一类。也是符合我们大众对编译认知的。编译在这个时间段经历了哪些过程呢? 词法、语法分析 词法分析是将源代码的字符流转变为Token集合,而语法分析则是根据Token序列抽象构造语法树(AST)的过程,AST是一种用来描述程序代码语法结构的树形表示形式,语法树的每个节点都代表着程序代码中的一个语法结构,例如包、类型、修饰符、运算符、接口、返回值甚至代码注释都可以是一个语法结构。 填充符号表 完成了语法和词法分析之后,下一步就是填充符号表的过程,符号表中所登记的信息在编译的不同阶段都要用到。在这里延伸一下符号表的概念。符号表是什么呢?它是由一组符号地址和符号信息构成的表格,最简单的可以理解为哈希表的K-V值对的形式。为什么会用到符号表呢?符号表最早期的应用之一就是组织程序代码的信息

C语言中volatile关键字的使用

こ雲淡風輕ζ 提交于 2019-12-13 12:56:44
volatile是一个类型修饰符(type specifier),就像我们熟悉的const一样,它是被设计用来修饰被不同线程访问和修改的变量;volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。 volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。 作用 编辑 简单地说就是防止编译器对代码进行优化。比如如下程序: 1XBYTE[2]=0x55; 2XBYTE[2]=0x56; 3XBYTE[2]=0x57; 4XBYTE[2]=0x58; 对外部硬件而言,上述四条语句分别表示不同的操作,会产生四种不同的动作,但是编译器却会对上述四条语句进行优化,认为只有XBYTE[2]=0x58(即忽略前三条语句,只产生一条机器代码)。如果键入volatile,则编译器会逐一地进行编译并产生相应的机器代码(产生四条代码)。 例子 编辑 精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子: 1)并行设备的硬件寄存器(如:状态寄存器) 2)一个中断服务子程序中会访问到的非自动变量(Non-automatic variables) 3)多线程应用中被几个任务共享的变量 这是区分C程序员和嵌入式系统程序员的最基本的问题

2019年12月12日

纵饮孤独 提交于 2019-12-12 22:59:17
一个由C/C++编译的程序占用的内存分为以下几个部分, 1、栈区(stack),由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。 2、堆区(heap),一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。 3、全局区(静态区(static),全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另 一块区域。 程序结束后由系统释放。 4、文字常量区 ,常量字符串就是放在这里的。 程序结束后由系统释放 5、程序代码区,存放函数体的二进制代码。 二、例子程序 这是一个前辈写的,非常详细 //main.cpp int a = 0; 全局初始化区 char *p1; 全局未初始化区 main() { int b; 栈 char s[] = “abc”; 栈 char *p2; 栈 char *p3 = “123456”; 123456\0在常量区,p3在栈上。 static int c =0; 全局(静态)初始化区 p1 = (char *)malloc(10); p2 = (char *)malloc(20); 分配得来得10和20字节的区域就在堆区。 strcpy(p1, “123456”);

Java中的逃逸分析

老子叫甜甜 提交于 2019-12-10 22:22:37
1:JIT(Just In Time Compiler即时编译器): 虚拟机为了提高执行热点代码(对于某个方法活代码块执行频率非常频繁时,认定这些代码时热点代码)的效率,在运行时会将这些代码编译为与本地平台相关的机器码,并进行各种层次优化,完成这个任务的编译器称为即时编译器。 2:对象逃逸: 它 并不是一种直接优化代码的手段,而是为其它优化手段提供依据的分析技术(通过此技术可以判定哪些代码可以被优化,如何被优化)。逃逸分析的基本行为就是分析对象动态作用域。一般分为两种方式: 方法逃逸和线程逃逸 方法逃逸: 当一个对象在方法中被定义后,它可能被外部方法所引用则称为方法逃逸。例如用该对象作为调用参数传递到其它方法中。 线程逃逸: 当一个对象在方法中被定义后如果能外部线程访问则为线程逃逸。比如将该对象赋值给类变量或可以在其它线程中访问该对象 如果能证明一个对象不会逃逸到方法或线程之外,也就是说别的方法或者线程无法通过任何途径访问这个对象,则可能对这个变量进行一些高效的优化 2.1栈上分配: 一般对象的内存空间是在堆上被分配,堆中数据对于所有线程都是可见的,只要拥有该对象引用,就可以访问堆中存储的数据。一般情况下虚拟机的垃圾回收机制大部分时间都在回收堆中对象,不论是确定对象是否需要被回收,还是回收对象整个过程都是需要cpu资源以及时间的(具体可参考垃圾回收机制)

C语言中volatile关键字的用法

谁都会走 提交于 2019-12-10 21:17:56
volatile是一个 类型修饰符 (type specifier),就像我们熟悉的const一样,它是被设计用来修饰被不同线程访问和修改的 变量 ;volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。 volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。 作用 编辑 简单地说就是防止编译器对代码进行优化。比如如下程序: 1 2 3 4 XBYTE[2]=0x55; XBYTE[2]=0x56; XBYTE[2]=0x57; XBYTE[2]=0x58; 对外部硬件而言,上述四条语句分别表示不同的操作,会产生四种不同的动作,但是编译器却会对上述四条语句进行优化,认为只有XBYTE[2]=0x58(即忽略前三条语句,只产生一条机器代码)。如果键入 volatile ,则编译器会逐一地进行编译并产生相应的机器代码(产生四条代码)。 例子 编辑 精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在 寄存器 里的备份。下面是volatile变量的几个例子: 1)并行设备的硬件寄存器(如:状态寄存器) 2)一个中断服务子程序中会访问到的非自动变量(Non-automatic variables) 3)多线程应用中被几个任务共享的变量 这是区分C程序员和 嵌入式系统

并发(JMM综述)

核能气质少年 提交于 2019-12-10 15:31:41
JMM综述 文章目录 JMM综述 一,内存模型产生背景 二,内存模型概念 2.1JMM组成部分 2.2JVM内存操作的并发问题 2.3内存交互操作流程 三,JMM深入 3.1原子性,可见性和有序性 3.2happens-before 3.3内存屏障 3.3重排序 四,volatile 4.1volatile内存语义 4.2volatile特性 4.3volatile如何禁止指令重排序 五,synchronize 六,final 一,内存模型产生背景 由于计算机的存储设备与处理器的运算速度有几个数量级的差距,为了避免处理器等待缓慢的内存读写操作完成,现代计算机系统通过加入一层读写速度尽可能无限接近处理器运算速度的高速缓存 缓存作为内存和cpu之间的缓冲区,将运算需要用到的数据放入到高速缓存中,让运算能快速运行,运算结束后在从缓存同步回内存之中 类似redis缓存和数据库一样,引入了高速缓存虽然解决了处理器和内存速度的差异,但是却带来一个新的问题-----缓存一致性问题 在多核处理器的系统中,每个处理器都有自己的高速缓存,他们共享同一内存,当多个处理器的运算任务都涉及同一块内存区域时,可能会出现缓存数据不一致的问题,需要使用缓存一致性协议来维护缓存的一致性 二,内存模型概念 java虚拟机提供java内存模型(JMM)来 屏蔽掉各种硬件和操作系统的内存访问差异 ,以实现

我用 Rust 重写了博客,谈谈我眼中的 Rust.

心不动则不痛 提交于 2019-12-10 05:58:34
前言 我已经学习 Rust 有挺长的一段时间了,一直想用 Rust 来写点东西,但是缺乏好的想法; 有些想法自我感觉良好,但是又没有能力实现 (比如,写一个操作系统?), 所以最后我决 定还是拿我的博客来做实验品吧。我原来的博客 是 基于 Github Page 和 Org mode 实现的静态博客,略显简单。所以我就觉得用 Rust 来写 个新的博客。 我眼中的 Rust 前后花了一个月的空余时间来写这个博客,写完这个博客之后,我觉得我应该算是有点 Rust 的编程经验了 ;-). 在学习 Rust 和使用 Rust 的时候,我总是将 Rust 和我平时使 用的语言进行对比。一段时间下来,我对使用 Rust 的感悟越来越多,所以我谈谈我眼中的 Rust 优点 内存安全 谈及 Rust, 首先想到的一定是内存安全,正如官网对 Rust 的定义: Rust is a systems programming language that runs blazingly fast, prevents segfaults, and guarantees thread safety. 这个也是 Rust 对比其他的语言最大的亮点之一. 众所周知,在 Rust 之前的其他编程语言 管理内存的方式无非是以使用垃圾回收算法管理内存(代表就是 Java 和 Python), 或者是 C/C++

为什么只能在头文件中实现模板?

纵然是瞬间 提交于 2019-12-07 08:38:09
引用来自 C ++标准库:教程和手册 : 目前使用模板的唯一可移植方法是通过使用内联函数在头文件中实现它们。 为什么是这样? (澄清:头文件不是 唯一的 可移植解决方案。但是它们是最方便的可移植解决方案。) #1楼 这里有很多正确的答案,但是我想补充一下(为了完整性): 如果您在实现cpp文件的底部,对模板将使用的所有类型进行显式实例化,则链接程序将能够照常查找它们。 编辑:添加显式模板实例化的示例。 在定义模板和定义所有成员函数之后使用。 template class vector<int>; 这将实例化该类及其所有成员函数(仅对链接器可用)。 类似的语法适用于模板函数,因此,如果您有非成员运算符重载,则可能需要对它们执行相同的操作。 上面的示例是毫无用处的,因为矢量完全在标头中定义,除非公共包含文件(预编译的标头?)使用 extern template class vector<int> 以避免在所有 其他 (1000?)文件中实例化它时都没有用使用向量。 #2楼 这是因为需要单独编译,并且模板是实例化样式的多态性。 让我们更接近具体的解释。 说我有以下文件: foo.h 声明 class MyClass<T> 的接口 foo.cpp 定义 class MyClass<T> bar.cpp 使用 MyClass<int> 独立汇编的手段,我应该能够从 bar.cpp 独立编译

Xcode中的 编译过程以及编译器

℡╲_俬逩灬. 提交于 2019-12-07 02:16:26
一。 在Xcode里写C语言函数的编译过程 1.编译,连接,运行 编译 1.什么是编译:将C源程序翻译成计算机能识别的0和1 2.使用什么编译器:3之前用GCC,Xcode4之后使用LLVM编译器(前端clang) 在终端输入:CC -c 文件名.c (cd 进入当前目录下) 编译成功,会生成.o目标文件 3.编译器的错误和警告功能: 如果代码有语法问题,编译器会直接报错 只要有一个错误❌,程序就不能编译成功,也不好生成.o文件 警告⚠️只是编译的一些建议,并不影响编译通过。 连接 1.连接的作用:将.o文件和C语言函数库组合在一起。生成可执行文件 2.由连接器完成:clang连接器里面已经包含了连接指令。 终端输入: cc 文件.o 连接成功,会生成a.out可执行文件。 运行 1.两种方式运行 * 直接双击打开a.out文件 * 终端中使用./a.out指令。 2修改文件内容后,一定要重新编译,连接,再运行。 总结 1整个程序的运行过程 编写程序 ->元到吗 -> 编译 -> 目标文件 -> 连接(C语言函数库)-> 可执行的机器代码 -> 运行 2开发中会遇到哪些错误?如何解决? 语法错误,可以通过编译器的报错信息解决 逻辑错误,需要耐心调试程序 3学习建议 学编程不是学英语 程序的可读性 二。 参考: // https://segmentfault.com/a

为什么只能在头文件中实现模板?

混江龙づ霸主 提交于 2019-12-06 16:52:19
引用来自 C ++标准库:教程和手册 : 目前使用模板的唯一可移植方法是通过使用内联函数在头文件中实现它们。 为什么是这样? (澄清:头文件不是 唯一的 可移植解决方案。但是它们是最方便的可移植解决方案。) #1楼 这里有很多正确的答案,但是我想补充一下(为了完整性): 如果您在实现cpp文件的底部,对模板将使用的所有类型进行显式实例化,则链接程序将能够照常查找它们。 编辑:添加显式模板实例化的示例。 在定义模板和定义所有成员函数之后使用。 template class vector<int>; 这将实例化该类及其所有成员函数(仅对链接器可用)。 类似的语法适用于模板函数,因此,如果您有非成员运算符重载,则可能需要对它们执行相同的操作。 上面的示例是毫无用处的,因为矢量完全在标头中定义,除非公共包含文件(预编译的标头?)使用 extern template class vector<int> 以避免在所有 其他 (1000?)文件中实例化它时都没有用使用向量。 #2楼 这是因为需要单独编译,并且模板是实例化样式的多态性。 让我们更接近具体的解释。 说我有以下文件: foo.h 声明 class MyClass<T> 的接口 foo.cpp 定义 class MyClass<T> bar.cpp 使用 MyClass<int> 独立汇编的手段,我应该能够从 bar.cpp 独立编译