Java学习篇(二)—— C++和Java的区别之程序内存分布
上一篇介绍了C++和Java编译的区别和Java独有的网络编程,线程管理。这一篇主要介绍一下两者在程序运行时的内存空间。
内存分布
| 项目 | C++ 程序 | Java 程序(使用 JVM) |
|---|---|---|
| 编译结果 | 直接生成机器码(如 .exe) |
编译成 .class 字节码 |
| 执行方式 | 操作系统直接加载执行 | 需要 JVM 加载 .class 文件 |
| 启动时发生的事 | 操作系统创建进程并加载程序 | 操作系统创建 JVM 进程,JVM 加载类 |
| 所属进程 | 你的 C++ 程序本身就是进程 | Java 程序是运行在 JVM 进程中的一段代码 |
| 内存分配方式 | 程序直接使用虚拟内存 | JVM 申请虚拟内存再分出堆、栈等区域 |
C++的程序启动过程,是操作系统创建一个用户进程,运行C++程序。一个程序就是一个新的用户进程,有自己独立的内存空间(虚拟)。
Java的程序启动过程,与C++相比较,只是多了JVM的启动过程。Java程序也是一个进程,但是JVM的“客人”,必须在 JVM 虚拟机中运行。
由此可以判断出,为什么Java的线程管理这么方便,因为它的线程管理不依赖于操作系统,自由度也就更高。
C++程序内存分布

+-------------------+ 高地址
| 栈区(Stack) | 局部变量、函数调用帧
+-------------------+
| 堆区(Heap) | new / malloc 分配的内存
+-------------------+
| 全局区(静态区)| static 和全局变量
+-------------------+
| 代码段(Text) | 程序指令
+-------------------+ 低地址
程序能够使用的最大内存为4GB,这个大小是PC指针能够寻址的最大范围,也是你的总线的位数。高位地址是内核空间,用来执行系统调用。在C/C++中内存分为5个区,分别为栈区、堆区、全局/静态存储区、常量存储区、代码区。
- 静态内存分配:编译时分配。包括:全局、静态全局、静态局部三种变量。
- 动态内存分配:运行时分配。包括:栈(stack): 局部变量。堆(heap): c语言中用到的变量被动态的分配在内存中。(malloc或calloc、realloc、free函数)
在 C++ 中,你创建的对象是放在「堆区」还是「栈区」,取决于你如何创建它。普通变量或自动对象创建在栈区,而new和malloc的对象则创建在堆区。
Java程序内存分布
Java 的内存分布由JVM管理,而C++的内存分布由操作系统直接管理。
Java的JVM是在操作系统虚拟内存之上的一个“虚拟机”,它向操作系统申请一块内存,然后自己划分成方法区、堆、栈等区域进行管理。Java虚拟机先运行,Java程序被“加载进去”。
+-------------------------+ 高地址
| 元空间 (Metaspace) | 存类信息、静态变量、常量池(JDK8之后)
| 方法区(永久代,已废弃)| 存储类结构、方法数据等(JDK7及以前)
+-------------------------+
| Java 堆(Heap) | 对象实例分配的内存,大部分对象都在这里
+-------------------------+
| 虚拟机栈(每线程一个) | 方法调用、局部变量表、操作数栈等
+-------------------------+
| 程序计数器 | 当前执行字节码的行号指示器
+-------------------------+
| 本地方法栈 | 用于执行 native 方法
+-------------------------+ 低地址

上图可知,Java的内存区包括两部分:线程私有部分———虚拟机栈、本地方法栈、程序计数器,公共部分——堆、本地内存。
Java方法和Native方法
Java 方法是使用 Java 语言编写的方法,最终被编译成字节码(.class 文件中的Code属性),由 JVM 直接解释或编译执行。Native 方法是使用非 Java 语言(通常是 C/C++)编写的方法,并通过 JNI(Java Native Interface) 在 JVM 中调用。
私有部分
程序计数器:这个和CPU的PC指针是一样的,CPU的PC指针指向当前运行指令的物理内存地址,后续取指令,解析指令,运行指令。线程的程序计数器记录的是正在执行的字节码指令地址,这是一个增量,基于当前正在执行的方法的字节码数组上增加。
虚拟机栈:每一个线程在执行代码时,都需要栈区来记录局部变量、返回地址等信息,Java方法调用都是通过虚拟机栈来实现的,栈由一个个栈帧组成,而每个栈帧中都拥有:局部变量表、操作数栈(栈顶存放有当前方法调用之后的返回值)、动态链接(调用当前方法所需的引用)、方法返回地址(方法返回后继续执行的位置,也就是调用方法时当前PC的值)。所以对于线程而言,若要调用一个方法,然后返回至原来的位置,流程是:
PC指针保存在当前栈帧的方法返回地址,创建新的栈帧,将参数保存在局部变量表,动态链接区对符号引用进行解析,将新的栈帧压栈。
PC指针指向新的方法,开始执行字节码,将产生的变量和操作数保存在局部变量表和操作数栈。
执行完方法后,将返回值保存在操作数栈栈顶。将栈帧弹出,PC指针恢复为新的栈顶的方法返回地址,从弹出栈帧的操作数栈栈顶取执行结果。
本地方法栈:和虚拟机栈所发挥的作用非常相似,区别是:虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。
每个线程所执行的“代码”(即类的字节码)在方法区或堆中共享,自己的“变量”在JVM 栈(局部变量表)中,当前线程正在执行的字节码位置保存在程序计数器中。当线程需要修改共享区域时,使用锁。
共有部分
| 比较项 | 堆(Heap) | 方法区(Method Area) |
|---|---|---|
| 存放内容 | 实例对象 | 类的元数据、方法信息、静态变量、常量池等 |
| 作用 | 运行时对象分配 | 加载类结构信息、JIT 编译缓存、静态数据 |
| 生命周期 | 随 JVM 存在 | 随 JVM 存在 |
| 垃圾回收 | 对象回收 | 类卸载时才回收(较少发生) |
上表用来比较堆和方法区的区别,也就是说方法区存在的只是方法,真正的实例化对象在堆,一个方法可以有很多个实例化对象。
- 堆:此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存。Java堆是垃圾收集器管理的主要区域,因此也被称作GC堆(Garbage Collected Heap)。GC堆通过分代垃圾收集算法,控制对象年龄,更好地回收内存,或者更快地分配内存。

字符串常量池:是 JVM 为了提升性能和减少内存消耗针对字符串(String 类)专门开辟的一块区域,主要目的是为了避免字符串的重复创建。JDK 1.7以后从永久代移动到了堆中,来实现高效的内存回收。
方法区:方法区属于是当虚拟机要使用一个类时,它需要读取并解析 Class 文件获取相关信息,再将信息存入到方法区。方法区会存储已被虚拟机加载的 类信息、字段信息、方法信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
运行时常量池:字面量是源代码中的固定值的表示法,即通过字面我们就能知道其值的含义。字面量包括整数、浮点数和字符串字面量。常见的符号引用包括类符号引用、字段符号引用、方法符号引用、接口方法符号。总的来说:编译器能确定的字面量和常量,符号引用。
符号引用(Symbolic Reference)是 Java 字节码中用于表示类、字段、方法等的一种 “符号形式” 的间接引用,在类未加载或链接前,它还不是一个内存地址或直接指针,而是“名字”+“描述符”等信息的组合。
- 直接内存:通过 JNI 的方式在本地内存上分配的。
参考资料
Java学习篇(二)—— C++和Java的区别之程序内存分布的更多相关文章
- Java学习笔记二十八:Java中的接口
Java中的接口 一:Java的接口: 接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明.一个类通过继承接口的方式,从而来继承 ...
- Java学习笔记二十五:Java面向对象的三大特性之多态
Java面向对象的三大特性之多态 一:什么是多态: 多态是同一个行为具有多个不同表现形式或形态的能力. 多态就是同一个接口,使用不同的实例而执行不同操作. 多态性是对象多种表现形式的体现. 现实中,比 ...
- Java学习笔记二十四:Java中的Object类
Java中的Object类 一:什么是Object类: Object类是所有类的父类,相当于所有类的老祖宗,如果一个类没有使用extends关键字明确标识继承另外一个类,那么这个类默认继承Object ...
- Java 学习笔记 (二) Selenium WebDriver Java 弹出框
下面这段实例实现了以下功能: 1. profile使用用户本地电脑上的 (selenium 3有问题.因为selenium 3把profile复制到一个temp文件夹里,但并不复制回去.所以每次打开仍 ...
- Java学习笔记二十六:Java多态中的引用类型转换
Java多态中的引用类型转换 引用类型转换: 1.向上类型转换(隐式/自动类型转换),是小类型到大类型的转换: 2.向下类型转换(强制类型转换),是大类型到小类型的转换: 3.instanceof运算 ...
- 【转】java提高篇(二)-----理解java的三大特性之继承
[转]java提高篇(二)-----理解java的三大特性之继承 原文地址:http://www.cnblogs.com/chenssy/p/3354884.html 在<Think in ja ...
- Java 学习(21):Java 实例
Java 实例 本章节我们将为大家介绍 Java 常用的实例,通过实例学习我们可以更快的掌握 Java 的应用. Java 环境设置实例 //HelloWorld.java 文件 public cla ...
- 最新java学习路线:含阶段性java视频教程完整版
最新java学习路线:带阶段性java视频教程版本 第一阶段:Java基础 学习目标: 掌握基本语法.面向对象.常用类.正则.集合.Io流.多线程.Nio.网络编程.JDK新特性.函数式编程 知识点细 ...
- Java 学习(7):java 日期时间 & 正则表达式
目录 --- 日期时间 --- 正则表达式 日期时间:java.util 包提供了 Date 类来封装当前的日期和时间. Date 类提供两个构造函数来实例化 Date 对象. 构造函数:用于初始化对 ...
- Java 学习(17): Java 泛型
Java 泛型 Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型. 泛型的本质是参数化类型,也就是说将 ...
随机推荐
- 工作日记-storm集群业务崩溃和解决方案
背景 昨天公司业务部门报告业务崩溃,查看各个业务节点后,定位问题到storm集群. 打开storm ui查看下任务状态,发现可以加载页面元素,但是无法加载数据,分析是nimbus挂掉了,重启nimbu ...
- Codeforces Round 944 (Div. 4)
知识点模块 1. ai xor aj<=4 意味着两个数字的二进制位,只能有后两位的二进制位不同,因为如果第三位二进制位不同,就会出现异或的结果大于4 2.要有化曲为直的思想 学会把曲线上的坐标 ...
- 自定义vscode 调试控制台字体颜色
"workbench.colorCustomizations": { "debugConsole.warningForeground": "#1818 ...
- MySQL安装入门第一篇
[1]MySQL的版本:近期主要历史版本有5.0/5.1/5.5/5.6/5.7,目前最新版本是MySQL8.6.0曾经是个内部试验版本,已取消了. MySQL8.0的版本历史 1) 2016-09- ...
- 一个开源的 Blazor 跨平台入门级实战项目
前言 今天大姚给大家分享一个开源(MIT license).免费的 Blazor 跨平台入门级实战项目:YourWeather. 项目介绍 YourWeather是一个开源(MIT license). ...
- 编译nwjs/node-webkit可用的sqlite3简单靠谱的解决方案/在nwjs里使用sqlite
大胸弟,如果你和我一样把官方给的方法和网上的文章都试过了还是编译不过,但又必须在nwjs里使用sqlite数据库,那么请继续往下看. 我的解决方法就是: 1.不编译 来吧,拥抱html5吧,具体来说就 ...
- nexus私有仓库与maven集成
首先搭建maven 下载maven,并解压,移动到/usr/local/,重命名为 maven-3.5.4 配置环境变量:vim /etc/profileexport MAVEN_HOME=/usr/ ...
- 在鸿蒙NEXT中开发一个2048小游戏
本项目是基于api12开发的2048游戏,游戏的逻辑是当用户向某个方向滑动时,将该方向相邻且相等的数字相加,同时在空白区域的随机位置生成一个随机数字.游戏中的数字越大,分数越高. 首先,游戏的界面布局 ...
- P1514 [NOIP 2010 提高组] 引水入城 题解
题意:P1514 [NOIP 2010 提高组] 引水入城有点复杂,自己看吧. 思路 这里提供一个好像没见过的纯 DP 做法,不需要神秘的证明以及任何脑子,直接顺着思路做即可. 首先判断正确性就是从第 ...
- 联邦学习图像分类实战:基于FATE与PyTorch的隐私保护机器学习系统构建指南
引言 在数据孤岛与隐私保护需求并存的今天,联邦学习(Federated Learning)作为分布式机器学习范式,为医疗影像分析.金融风控.智能交通等领域提供了创新解决方案.本文将基于FATE框架与P ...