总览
主要讲了 c++ 和 Java 两种语言内存空间分布上的不同以及为什么不同
Java 内存区域
先上图
接下来一块一块来讲
一、c++ 中没有的部分
本地方法栈
我们知道本地方法栈里存的是 native 方法的调用栈,也就是底层的 c/c++ 方法
问题一:Java 为什么为什么要调用本地方法?
我们知道 Java 很经典的那句话 "write once, run anywhere",Java 的运行依赖于 JVM。
而 JVM 本质上就是在底层操作系统上又加了一层,才实现了跨平台的能力,但是也带来了一些缺点:
- 无法直接操作底层硬件资源:比如直接操作内存、文件系统、硬件设备等
- 性能瓶颈:class 文件是要经过 JVM 来解释执行的,怎么比得过 c++ 编译完二进制直接执行呢?当然 JVM 也做了一些优化,例如 JIT 等
所以需要 native 方法来帮我解决上面的一些问题,JVM 本身是由 c++ 实现的
字节码执行的本质是被一个 c++写的虚拟机转换为机器码来执行
问题二:Java 为什么要有本地方法栈?
个人理解主要的原因:一是需要进行环境隔离,避免相互干扰(虚拟机栈和本地方法栈);二是线程隔离,每个线程都有两个栈,本地方法的执行不会影响 Java 栈。
所以 Java 有native 方法栈,但是 c++因为编译型语言,直接运行在操作系统上,不需要虚拟机,而是由编译器来处理,所以不需要本地方法栈。
程序计数器PC
JVM 中的程序计数器是用来记录当前线程执行的字节码指令地址,存储的是字节码指令的地址而不是机器码地址。
而 c++ 的程序计数器是 CPU 的物理寄存器,由操作系统和硬件直接管理。
这里说点人能听懂的话哈哈哈
我们前面提到了 JVM 其实是在操作系统之上又加了一层,所以原本操作系统本身的 PC 是没法使用的,相当于我们在 JVM 里面手动实现了一个 PC,完毕
二、c++ 和 Java 对比的部分
回顾上一篇,c++ 有四个区域:代码区、全局区、栈区、堆区,然后来比较下
代码区 vs JVM 方法区(元空间)
c++代码区 | Java元空间 |
---|---|
存放程序的机器指令(编译后的二进制代码) | 存放类信息、常量池、静态变量、方法字节码等 |
代码区是只读的,不可以修改 | 可以在运行时动态加载类 |
方法区可以看做 Java 对代码区的拓展,不仅包含代码,还包含了运行时常量池和类的元数据
全局区 vs JVM方法区 + Java堆中的字符串常量池
回顾一下上一篇,全局区里存了什么?
我们依次看下这些数据在 Java 里都存在什么地方呢?
全局变量 or 静态变量:Java 里没有全局这个概念,但是可以认为 static 实现类似功能(类级别),存在方法区中
全局常量: Java 中对应 static final 字段,也是存在于方法区中
字符串常量 :对应字符串常量池 ,最开始在方法区,后面被移动到 堆内存 中
栈区 vs Java虚拟机栈
本质上是类似的,但是由前面本地方法栈的分析可得:
c++ 的栈是由编译器自动管理的,而 Java 由于 JVM 这一层的原因,是有 JVM 来管理我们的虚拟机栈
堆区 vs JVM 堆
在 c++ 中,此区域由程序员手动管理(new/delete, malloc/free),用于动态分配对象,需要我们手动释放内存,否则会造成内存泄露。
Java 中由 JVM 自动管理,通过 GC 来自动回收内存,不需要程序员管理
c++手动 vs JVM自动
讲到这里了,就再讲讲手动和自动的区别吧
个人理解,手动主要是性能:
- 没有 GC 开销,包括 GC 停顿以及 GC roots 的扫描
- 零拷贝优化:直接操作内存地址,减少数据拷贝 包括其他的 cache 对齐等优化
自动主要是容错:
- 手动的话不释放会有内存泄露
- 对象量很多的话,显式调用太麻烦,很容易出错、遗漏
- 缓冲区溢出:手动操作越界等等
因为楼主之前一直用的是 Java,其实没有自己去过申请、释放过内存,希望后续的c++学习中能够有更加深入的了解