0

今年我开始使用 Java 编程。我了解高级概念并且对编程感到自在。

但是我似乎一直在问我所有这些在内部是如何工作的?我知道 Java 是一种高级语言,特别是为了让程序员远离低级的东西以减轻开发。

本质上,我想更多地了解高级语言在内部是如何运作的(例如面向对象的编程)。我很清楚为什么要使用它们,但是现在一切在内部如何工作(内存分配等)。对象如何在内部呈现等。

有人可以用一些关键字为我指明正确的方向,或者最好参考一些材料吗?学习像 C 或 C++ 这样的低级语言会有助于这个学习过程吗?

4

2 回答 2

1

根据你问题的措辞,你的低级还是很高的。

面向对象与语言的高低无关,它只是在对象上的意思,你可以进行面向对象的汇编。它不是语言的东西,基本上任何语言都可以以面向对象的方式使用。

内存分配特定于操作系统和/或管理内存的任何人。在高层次上没有什么复杂的。我有一个披萨,三个人,我可以把披萨切成 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 个相邻的宿舍里,当需要与这五个学生中的每一个交谈时,我们可以使用一张名字表来宿舍找到他们。虚拟地址是按字母顺序排列的列表,物理地址是这些人居住的宿舍。管理表格,程序可以访问它认为的线性内存空间,但实际上只是散布的碎片。您不必在分配和释放内存时对其进行“碎片整理”。没有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(早期的帕斯卡等)通过编译成机器代码来抽象它,该机器代码实际上并没有在硬件中实现,也不能直接在硬件中实现。然后拥有一个平台并运行特定的虚拟机(用其他语言如 C 编写)读取这些 java 字节码,然后执行该任务,其中一些任务是 push b、push c、add (a),还有一些是 go read a文件。可以反汇编并查看 JAVA 在字节码级别生成的内容,但使用编译语言更容易做到这一点。

javiergarval 对 Tanenbaum 书籍或类似书籍的回答可能涵盖了您最初在中间层(操作系统)之后的内容。但取决于你想走多低,进入汇编语言,然后进一步进入逻辑和总线。

你可以考虑一下 Petzold 的书 Code: The Hidden Language of Computer Hardware and Software。从另一个方向来。

于 2016-11-02T06:09:53.873 回答
0

我一直推荐的一本好书是Andrew S. Tanenbaum 的Modern Operating Systems

它涵盖了您在编程时想知道的所有方法。

还有来自SO的这个线程。

于 2016-10-21T12:39:31.700 回答