C++与java内存区别
C++与Java,在内存分配和垃圾收集上区别很大
C++程序员拥有对内存管理有着很高的权利,控制一个对象从开始到结束的全过程
new delete/free 需要程序员注意内存的分配,不然容易出现内存泄漏及溢出
Java的内存由虚拟机自动内存管理,垃圾收集,降低了内存泄漏及溢出的可能性
java虚拟机内存区域
1.方法区
Mehtod Area
解释:
各个线程共享的内存区域,存储被虚拟机加载的
类信息
、
常量
、
静态变量
、即时编译器编译后的
代码
Java虚拟机规范把方法区描述为堆的一个逻辑部分,但其还有一个名称
非堆
HotSpot虚拟机把GC分代收集扩展到方法区,所以有人称之为
永久代
(Permanment Generation),实现方法区属于虚拟机实现细节,不受规范约束,使用永久带来实现方法区并不是好主意,因为
更容易内存溢出
,永久代有 -XX:MaxPermSize上限,HotSpot也逐渐在放弃永久代,1.7已将永久代的字符串常量池移出
Java虚拟机对方法区的限制非常宽松,不需要连续空间,可固定大小或扩展,可不实现垃圾收集;这区域的回收主要是针对常量池和对类型的卸载
2.Java堆
Heap
Java虚拟机管理内存中
最大
的一块,
所有线程共享
的一块内存区域
虚拟机启动时创建,唯一目的:
存放对象实例
- 几乎所有对象实例都在这里分配
- Java虚拟机规范:所有对象实例以及数组都要在堆上分配
- 随着JIT编译器的发展与逃逸分析技术成熟,栈上分配、标量替换优化技术导致对象在堆上分配不那么绝对
Java堆是
垃圾收集器
管理的主要区域,也叫GC堆(Garbage Collection Heap)
目前收集器基本采用
分代收集算法
,分新生代和老年代,更细致分为Eden空间、From Survivor空间、To Survivor空间
Java堆可以处于物理上不连续的内存空间
Java堆可以是固定也可以是可扩展的,通过-Xmx和-Xms控制
3.虚拟机栈
VM Stack
虚拟机栈描述的是java方法执行的
内存模型
每个方法执行时都会创建一个
栈帧Stack frame
- 局部变量表
- 存放编译器可知八大基本类型
- long和double占用两个局部变量空间Slot
- 在编译期间完成分配,进入方法时已确定
- 对象引用 reference
- returnAddress(指向一条字节码指令地址)
线程请求的
栈深度超过
所允许的深度将抛出
*Error
若是可虚拟机栈
自动扩展
的虚拟机,抛出
OutOfMemoryError
4.本地方法栈
Native Method Stack
与Java虚拟机栈类似,只不过VM Stack为
Java方法(字节码)
服务,Native Method Stack为
Native方法
服务
Java虚拟机规范对本地方法语言、使用方式与数据结构无强制规定,虚拟机 可*实现它,如HotSpot直接将二者合二为一,本地方法区也会抛出
栈溢出
和
内存溢出
异常
5.程序计数器
Program Counter Register
较小空间,当前线程执行的字节码的
行号指示器
字节码解释器
工作时就是改变这个计数器的值来执行下一条字节码指令,分支、循环、跳转、异常、线程恢复都依赖这个计数器
线程都有
独立
的程序计数器,各线程相互独立,这是线程的
私有内存
当前线程执行的若是Java方法,则计数器记录字节码指令地址
若是Native方法,则计数器值为空(Undefined)
此内存区域是唯一一个在Java虚拟机规范中没有OutOfMemoryError情况的区域
运行时常量池
Runtime Constant Pool
方法区的一部分,
Class
文件除了有类的
版本
、
字段
、
方法
、
接口
等描述信息,还有一项就是
常量池
,存放编译期生成的
各种字面量
和
符号引用
Java虚拟机对类文件的每一部分格式都有严格规定,每个字节存储哪种数据都有规范,才能被虚拟机认可,装载、执行,但对运行时常量池没有任何细节的要求,不同虚拟机可不同实现
存放 : 编译器生成的字面量、类文件的符号引用、翻译出来的直接引用,运行期间的新常量
直接内存
Direct Memory
堆外内存,也可能导致OutOfMemory
1.4引入的NIO,是基于通道Channel和缓冲区Buffer的I/O方式,可以使用Native方法库直接分配堆外内存,通过堆中的DirectByteBuffer对象作为这块内存的引用来操作,在一些场景中提高性能,避免Java堆和Native堆来回复制数据
参考资料:深入理解Java虚拟机-JVM高级特性与最佳实践 第2版