程序运行时,我们最好对数据保存到什么地方做到心中有数。特别要注意的是内存的分配。有六个地方都可以保存数据:
(1) 寄存器。这是最快的保存区域,因为它位于和其他所有保存方式不同的地方:处理器内部。然而,寄存器的数量十分有限,所以寄存器是根据需要由编译器分配。我们对此没有直接的控制权,也不可能在自己的程序里找到寄存器存在的任何踪迹。
(2) 栈(stack)。存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)或者常量池中(字符串常量对象存放在常量池中。) 驻留于常规RAM(随机访问存储器)区域,但可通过它的“堆栈指针”获得处理的直接支持。堆栈指针若向下移,会创建新的内存;若向上移,则会释放那些内存。这是一种特别快、特别有效的数据保存方式,仅次于寄存器。创建程序时,Java编译器必须准确地知道堆栈内保存的所有数据的“长度”以及“存在时间”。这是由于它必须生成相应的代码,以便向上和向下移动指针。这一限制无疑影响了程序的灵活性,所以尽管有些Java数据要保存在堆栈里——特别是对象句柄,但Java对象并不放到其中。
(3) 堆(heap)。存放所有new出来的对象,一种常规用途的内存池(也在RAM区域),其中保存了Java对象。和堆栈不同,“内存堆”或“堆”(Heap)最吸引人的地方在于编译器不必知道要从堆里分配多少存储空间,也不必知道存储的数据要在堆里停留多长的时间。因此,用堆保存数据时会得到更大的灵活性。要求创建一个对象时,只需用new命令编制相关的代码即可。执行这些代码时,会在堆里自动进行数据的保存。当然,为达到这种灵活性,必然会付出一定的代价:在堆里分配存储空间时会花掉更长的时间!
(4) 静态存储。存放静态成员(static定义的),这儿的“静态”(Static)是指“位于固定位置”(尽管也在RAM里)。程序运行期间,静态存储的数据将随时等候调用。可用static关键字指出一个对象的特定元素是静态的。但Java对象本身永远都不会置入静态存储空间。
(5) 常数存储。存放字符串常量和基本类型常量(public static final)。 常数值通常直接置于程序代码内部。这样做是安全的,因为它们永远都不会改变。有的常数需要严格地保护,所以可考虑将它们置入只读存储器(ROM)。
(6) 非RAM存储。若数据完全独立于一个程序之外,则程序不运行时仍可存在,并在程序的控制范围之外。其中两个最主要的例子便是“流式对象”和“固定对象”。对于流式对象,对象会变成字节流,通常会发给另一台机器。而对于固定对象,对象保存在磁盘中。即使程序中止运行,它们仍可保持自己的状态不变。对于这些类型的数据存储,一个特别有用的技巧就是它们能存在于其他媒体中。一旦需要,甚至能将它们恢复成普通的、基于RAM的对象。Java 1.1提供了对Lightweight persistence的支持。未来的版本甚至可能提供更完整的方案
这里我们主要关心栈,堆和常量池,对于栈和常量池中的对象可以共享,对于堆中的对象不可以共享。栈中的数据大小和生命周期是可以确定的,当没有引用指向数据时,这个数据就会消失。堆中的对象的由垃圾回收器负责回收,因此大小和生命周期不需要确定,具有很大的灵活性。
对于字符串:其对象的引用都是存储在栈中的,如果是编译期已经创建好(直接用双引号定义的)的就存储在常量池中,如果是运行期(new出来的)才能确定的就存储在堆中。对于equals相等的字符串,在常量池中永远只有一份,在堆中有多份。
如以下代码:
Java代码
String s1 = "china";
String s2 = "china";
String s3 = "china";
String ss1 = new String("china");
String ss2 = new String("china");
String ss3 = new String("china");
这里解释一下黄色这3个箭头,对于通过new产生一个字符串(假设为”china”)时,会先去常量池中查找是否已经有了”china”对象,如果没有则在常量池中创建一个此字符串对象,然后堆中再创建一个常量池中此”china”对象的拷贝对象。这也就是有道面试题:String s = new String(“xyz”);产生几个对象?一个或两个,如果常量池中原来没有”xyz”,就是两个。
对于基础类型的变量和常量:变量和引用存储在栈中,常量存储在常量池中。
如以下代码:
Java代码
int i1 = 9;
int i2 = 9;
int i3 = 9;
public static final int INT1 = 9;
public static final int INT2 = 9;
public static final int INT3 = 9;
对于成员变量和局部变量:成员变量就是方法外部,类的内部定义的变量;局部变量就是方法或语句块内部定义的变量。局部变量必须初始化。
形式参数是局部变量,局部变量的数据存在于栈内存中。栈内存中的局部变量随着方法的消失而消失。
成员变量存储在堆中的对象里面,由垃圾回收器负责回收。
如以下代码:
Java代码
class BirthDate {
private int day;
private int month;
private int year;
public BirthDate(int d, int m, int y) {
day = d;
month = m;
year = y;
}
省略get,set方法………
}
public class Test{
public static void main(String args[]){
int date = 9;
Test test = new Test();
test.change(date);
BirthDate d1= new BirthDate(7,7,1970);
}
public void change1(int i){
i = 1234;
}
}
对于以上这段代码,date为局部变量,i,d,m,y都是形参为局部变量,day,month,year为成员变量。下面分析一下代码执行时候的变化:
1. main方法开始执行:int date = 9;
date局部变量,基础类型,引用和值都存在栈中。
2. Test test = new Test();
test为对象引用,存在栈中,对象(new Test())存在堆中。
3. test.change(date);
i为局部变量,引用和值存在栈中。当方法change执行完成后,i就会从栈中消失。
4. BirthDate d1= new BirthDate(7,7,1970);
d1 为对象引用,存在栈中,对象(new BirthDate())存在堆中,其中d,m,y为局部变量存储在栈中,且它们的类型为基础类型,因此它们的数据也存储在栈中。 day,month,year为成员变量,它们存储在堆中(new BirthDate()里面)。当BirthDate构造方法执行完之后,d,m,y将从栈中消失。
5.main方法执行完之后,date变量,test,d1引用将从栈中消失,new Test(),new BirthDate()将等待垃圾回收。
相关推荐
### Java堆、栈和常量池——内存剖析 #### 寄存器 寄存器作为最快的存储区域之一,由编译器自动管理分配与回收,它位于CPU内,用于存储临时变量,例如局部变量和一些操作数。由于寄存器的数量有限且由编译器自动...
在深入探讨Java中的堆、栈以及常量池之前,我们先来简要回顾一下Java内存模型的基本概念。Java程序运行时会使用到不同的内存区域来存储各种类型的数据,这些区域包括:寄存器、虚拟机栈、本地方法栈、Java堆、方法区...
通过对Java中堆和栈的介绍及示例分析,我们可以看到两者在Java程序中的重要作用。掌握这些基础知识有助于更好地理解和优化程序的性能。 在实际开发过程中,合理利用堆和栈的特点能够有效提升程序的运行效率和资源...
关于Java栈与堆的深入解析 Java作为一种广泛使用的编程语言,其内存管理机制是学习者必须掌握的核心概念之一。在Java中,栈(Stack)与堆(Heap)是用于存储数据的主要区域,它们各自承担着不同的职责,对于理解...
### 深层解析Java虚拟机中的栈与堆:对象的内存分布 #### 核心概念:栈与堆的本质及作用 在Java编程语言中,理解栈(stack)和堆(heap)的概念及其工作原理对于深入掌握Java虚拟机(JVM)如何管理内存至关重要。栈和堆...
这些区域包括寄存器、栈、堆、常量池、代码段和数据段。理解这些内存区域的工作原理对于深入学习Java编程至关重要。 1. **寄存器**:JVM内部的虚拟寄存器,用于快速存取数据,但程序无法直接控制。 2. **栈**:栈...
本文将聚焦于Java内存分配的核心概念,包括栈、堆、常量池等关键区域的功能与使用场景,帮助读者深入理解Java内存管理的内在逻辑。 ### 一、Java内存模型概述 Java内存模型主要包括以下几个部分: 1. **寄存器**...
Java Class文件解析是Java开发中的一个重要概念,它涉及到Java虚拟机(JVM)如何理解和执行程序的核心机制。这篇博文的链接虽然无法直接访问,但从标题和标签我们可以推测,内容可能涵盖了对Java字节码的深入理解和...
此外,Java还有其他内存区域,如方法区(存放类信息、常量池等),以及本地方法栈(为Java Native Interface,JNI,调用的本地方法服务)。这些内存区域共同构成了Java虚拟机(JVM)的内存模型。 总结来说,Java的...
Java虚拟机的内存区域分为几个部分,其中包括线程私有的区域(程序计数器、Java虚拟机栈、本地方法栈)和线程共享的区域(Java堆、方法区、运行时常量池)。对象的访问是通过句柄或直接指针的方式来进行的。 在了解...
Java内存模型主要分为以下几个部分:寄存器、栈、堆、静态域、常量池以及非RAM存储(例如硬盘等永久存储空间)。本文将详细介绍这些组成部分及其各自的功能特点,帮助初学者更好地理解Java程序内部对象创建与消除的...
在实际开发中,我们还需要关注JVM的其他重要概念,如类加载机制(加载、验证、准备、解析、初始化)、类文件结构(魔数、版本号、常量池等)、垃圾回收算法(如标记-清除、复制、标记-整理、分代收集)以及执行引擎...
- **运行时数据区**:阐述了PC寄存器、Java虚拟机栈、Java堆、方法区、运行时常量池和本地方法栈等核心概念。 - **栈帧结构**:详解栈帧的局部变量表、操作数栈、动态链接、方法调用完成等组成部分。 - **对象的...
在程序运行过程中,常量池会存储在方法区(Method Area)而非堆中。 - **直接常量**:如字符串、整数等。 - **符号引用**:如类和接口的全限定名、字段和方法的名称及其描述符。 #### 四、堆与栈的区别 - **堆**...
字面量创建的String会存储在常量池中,而`new String()`则会在堆上创建。两者之间的差异可能导致内存效率问题,因此在编程时需谨慎选择。 String的连接操作也是一个常见的性能话题。使用`+`操作符连接字符串会创建...
这份压缩包包含了一份详细的PDF文档,全面覆盖了Java编程语言在校园招聘面试中的常见问题、答案以及深入解析,旨在帮助应聘者在面试中展现出扎实的技术功底。 一、Java基础篇 1. Java发展历程:从Sun Microsystems...