文章目录
- 1 两个重要的问题
- 2 C语言中数据的存储区
- 3 Java中对象的储存区
- 4 Java为什么采用动态内存分配?
- 4 为什么基本类型是特例?
1 两个重要的问题
- 对象的数据位于何处?
- 如何控制对象的生命周期?
2 C语言中数据的存储区
- 栈内存(Stack):主要是用来存储函数调用(function calls)和局部变量(local variables) 的内存区,它在内存中的数据结构本质就是一个栈(Stack)。需要注意的是,如果栈内存使用过多,就会出现所谓的堆栈溢位(Stack overflow),如死递归。
- 堆内存(Heap):主要是用来存储由
malloc()
申请的内存。如果malloc()
返回**NULL
**的话就表明内存用完。 - 静态内存(Static):它的生命周期与程序相同,即在进程创建是被创建,在程序退出时被销毁。全局作用域变量(global variables), 文件作用域变量(file scope variables)和被
static
修饰的变量会储存在这里。 - 寄存器(Register):这个关键字的作用是请求编译器尽可能的将变量存在CPU内部寄存器中,而不是通过内存寻址访问,以提高效率。需要注意的是:register这个关键字只是一个请求,请求CPU给你一个内存,给不给还在于CPU。
3 Java中对象的储存区
- 栈:通过堆栈指针来操纵内存,指针下移则分配内存,指针上移则释放内存, 如图(图片来源于网络)
- 在程序创建的时候,必须知道储存在堆栈内所有的数据项,以便可以操纵指针,这样的约束就限制了Java的灵活性,所以部分数据存储在此,特别是对象的引用,而对象本身不储存并在其中。
- 堆:用于存放所有的Java对象,不同于栈,它不需要知道数据在堆中的生存期,需要的时候便
new
一个对象出来,那么如何释放掉内存呢?不得不提的是Java中的垃圾回收机制了,不像C/C++需要手动释放内存,垃圾回收机制会自动地检测并释放无用的内存,避免了程序的内存泄漏问题。 - 常量储存:常量值一般直接存放在程序代码的内部,这样做是很安全的,因为是常量值,所以他们不希望被改变,也不会被改变。
- 寄存器:(编程人员不能控制,也感知不到,不做讨论)
- 非RAM储存:储存在磁盘上的对象。
4 Java为什么采用动态内存分配?
C/C++认为效率的控制是很重要的,那么在设计者在实现的时候给予了编程人员选择的权力,为了追求更快的速度和更高的效率,对象的存储空间和生命周期在编写程序的时候就确定,这种方式将存储空间的分配和释放放置于优先考虑的位置,但是某些情况这种方式也牺牲了灵活性,于是就有第二种方式。
第二种存储方式就是在堆内存中动态的创建,这种方式中,直到运行程序的时候才知道需要多少对象,才知道它们的生命周期及其属性,在需要的时候也可以在此时刻创建,即储存空间是动态管理的,但是这种方式需要花费大量的时间在堆中分配存储空间即释放内存空间,这可能要远远大于在堆栈中创建储存空间的时间,在堆栈中创建储存空间和释放储存空间各需要一条汇编指令即可。
所以Java设计者认为动态操作的方式虽然速度效率不及前者,但是在复杂的程序设计中,这种灵活方式反而提升了速度和效率,所以Java完全采用了动态内存分配方式(基本类型是特例)。
4 为什么基本类型是特例?
Java中的基本类型:boolean
、byte
、char
、short
、int
、long
、float
、double
、(void)
boolean b = false;
int i = 0;
double d = 9.26;
如果基本类型往往是特别小而简单的变量,如果 new
所以和C/C++一样,Java对待基本变量,把它储存在堆栈中,更具有效率。
为了让基本类型具有变量的特性,Java中也有相应的包裹类型,来应对不同的需求:
基本类型 | 包裹类型 |
boolean | Boolean |
byte | Byte |
char | Character |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
void | Void |
参考:
《Thinking in Java》 - Bruce Eckel