我今年开始用Java编程。我理解高级概念,感觉舒适的编程。
但是我似乎一直在问我这一切是如何在内部工作的?我知道Java是一种高级语言,特别是为了让程序员远离低级别的东西来缓解开发。
本质上,我想更多地了解高级语言在内部的功能(例如面向对象的编程)。我很清楚为什么使用它们,但现在 内部的一切如何工作(内存分配等)。如何在内部呈现对象等。
有人可以通过一些关键字向我指出正确的方向,或者最好参考一些材料吗?学习像C或C ++这样的低级语言是否有助于这个学习过程?
答案 0 :(得分:1)
根据你问题的措辞,你的低级别仍然很高。
面向对象与语言的高度和低度无关,它只是意味着对象,你可以有面向对象的程序集。它不是一种语言,基本上任何语言都可以以面向对象的方式使用。
内存分配特定于操作系统和/或管理内存的任何人。没有什么比这更复杂的了。我有一个比萨饼和3个人,我可以用3片或4或8片或者其他什么来削减比萨饼,每个人可以分配一片,剩下一些,他们可以回来分配更多。现在,在消费后释放披萨分配并不是我们想要想象的东西。但是这个想法是一样的,你有一些你希望允许程序借用/拿走的记忆。你把它分开,不必是所有均匀的尺寸。你可以提供各种尺寸的1K,2K,4K,8K ...... 1Meg单位等,以及它们的倍数。你创建一个表/图表,表明谁已经消费了什么,什么是免费的。然后把它还给你,让它们自由。旧学校线性思维可以使这很难,但MMU(内存管理单元)使这很容易。这是低级或低级思维。它们是地址转换器以及保护功能,以防止程序访问不属于他们的内存。
从内存分配的角度来看,一个简单的方法来查看MMU的作用是想到所有可用的借用/占用内存都是以0x1000字节为单位。假设从地址0x10000开始,所以0x10000,0x11000,0x12000等等。那是实际内存端的物理地址。但我们也可以拥有一个虚拟地址空间。我可能要求0x3000字节,并可能给出一个指针0x20000000。当我在0x20000000和0x20000FFF之间访问时,mmu可以将该虚拟地址转换为物理地址0x00007000到0x00007FFF。但0x20001000到0x20001FFF可能会转换为物理0x00004000到0x00004FFF。自然0x20002000到其他一些物理地址。因此,如果某人分配了10个块,另一个分配3,管理该分配的软件可以将前10个物理块分配给第一个程序,然后接下来的3个分配给下一个,如果第一个释放,那么有人分配7个前7个物理可以给那个新的人给我们一个在物理线性视图中使用的前7个,3个免费和3个地图。如果有人现在分配4,我们实际上可以给他们3和最后一个,因为我们可以将它们映射到虚拟空间,这样他们就会觉得他们是线性访问它们。
如果我有一个按字母顺序列出的学生名单,这并不意味着他们的宿舍数字线性匹配。按字母顺序排列的学生名单上的1号学生不必住在1号宿舍。我有一张桌子,将他们的名字映射到他们的宿舍。如果我们按字母顺序在列表中间添加学生,并不意味着我们必须将所有宿舍房间号码洗牌,我们只需要一张桌子。因此,有人可以从字母顺序列表中选择5个名称来处理项目,这并不意味着他们在5个相邻的宿舍中,当需要与这5个学生中的每个人交谈时,我们可以使用名称表来宿舍找到他们。虚拟地址是按字母顺序排列的列表,物理地址是那些人居住的宿舍。管理表和程序可以访问它认为是线性内存空间的内容,但实际上只是传播的片段。你没有" defrag"内存,因为它被分配和释放。没有mmu,它会变得非常混乱。
高级语言避免的低级别的东西是处理器的细微差别。我可以通过驱动器订购汉堡,或者我可以去买包子,肉,泡菜,西红柿,生菜,番茄酱等,然后自己做饭和组装汉堡。高级语言中的a = b + c最终可能是一些内存和/或寄存器访问,以便将一个或多个寄存器保存到堆栈中,这样您就可以释放寄存器来收集存储在内存中的值(如果尚未在所述寄存器中执行操作,则现在或以后根据需要将结果保存到存储器中。系统调用如打印或文件访问或网络或视频等,大量代码执行小的单个任务来构成整体。所有的砖块,木板,钉子和水泥,以及建造一个像汉堡一样的建筑,都可以买一个人(编译器)建造的房子,或者我可以购买五亿件工具和材料并构建房屋塑造并按正确的顺序梳理这些材料。
高级语言也为您提供抽象。这是C,但我打赌你能理解它。
unsigned int fun ( unsigned int a, unsigned int b )
{
return(a+b+7);
}
我可以把它编成泡菜,生菜和面包配料以及把它们放在一起的刀和煎锅:
00000000 <fun>:
0: e52db004 push {fp} ; (str fp, [sp, #-4]!)
4: e28db000 add fp, sp, #0
8: e24dd00c sub sp, sp, #12
c: e50b0008 str r0, [fp, #-8]
10: e50b100c str r1, [fp, #-12]
14: e51b2008 ldr r2, [fp, #-8]
18: e51b300c ldr r3, [fp, #-12]
1c: e0823003 add r3, r2, r3
20: e2833007 add r3, r3, #7
24: e1a00003 mov r0, r3
28: e24bd000 sub sp, fp, #0
2c: e49db004 pop {fp} ; (ldr fp, [sp], #4)
30: e12fff1e bx lr
我可以提高麦当劳的效率,而不是油腻的勺子餐厅:
00000000 <fun>:
0: e2811007 add r1, r1, #7
4: e0810000 add r0, r1, r0
8: e12fff1e bx lr
或者我可以在完全不同的计算机上使用相同的代码:
00000000 <_fun>:
0: 1166 mov r5, -(sp)
2: 1185 mov sp, r5
4: 1d40 0006 mov 6(r5), r0
8: 65c0 0007 add $7, r0
c: 6d40 0004 add 4(r5), r0
10: 1585 mov (sp)+, r5
12: 0087 rts pc
是的,使用正确的工具(gnu工作正常),您可以轻松地使用C / C ++并开始查看上述内容并尝试理解它。语言为你做了什么。当涉及到诸如printf或文件访问等系统调用时。应用程序调用其他代码链接的库函数,并最终要求操作系统执行该任务(使用您的信用卡购买汉堡而不是现金,收银员现在必须在一个盒子里刷卡,盒子在世界的某个地方与银行交谈,请为我做这笔交易,而不是打开抽屉,收银员照顾它。添加几个数字通常不涉及操作系统,但是访问受控或复杂或共享的资源(如视频或磁盘等),您必须向操作系统询问,这是语言,编译器和操作系统特定的。
Java和python(早期的pascal等)通过编译到一个实际上没有实现的机器代码,也没有直接在硬件中实现。然后有一个平台和操作特定的虚拟机(用其他语言编写,如C)读取那些java字节码然后执行该任务,一些任务是推b,推c,添加(a),一些正在读取文件。可以反汇编并查看JAVA在字节码级别生成的内容,但更容易使用编译语言。
javiergarval回答了Tanenbaum的书或类似的书,它可能涵盖了你最初的中间层,即操作系统之后的内容。但是根据你想要的程度,你可以进入汇编语言,然后再进入逻辑和总线。
你可能会考虑这本书 代码:计算机硬件和软件的隐藏语言 由Petzold。来自另一个方向。
答案 1 :(得分:0)