Skip to content
🗂️ 文章分类: 后端  
🏷️ 文章标签: Java  
📝 文章创建时间: 2020-10-12
🔥 文章最后更新时间:暂无

[toc]

Java中的数据存储位置 堆,栈,静态区、常量池

java程序运行时,有6个地方都可以保存数据:

1、 寄存器:它位于和其他所有保存方式不同的地方:处理器内部。我们对此没有直接的控制权,也不可能在自己的程序里找到寄存器存在的任何踪迹。

2、 栈:存放基本数据类型的数据和对象的引用(指向对象位置的指针),但对象本身不存放在栈中,而是存放在堆中(new 出来的对象)。驻留于常规RAM(随机访问存储器)区域。

3、 堆:存放用new产生的数据(对象存储的数据)。一种常规用途的内存池(也在RAM区域),其中保存了java对象。和栈不同:用堆保存数据时会得到更大的灵活性。要求创建一个对象时,只需用new命令编制相碰的代码即可。执行这些代码时,会在堆里自动进行数据的保存。当然,为达到这种灵活性,必然会付出一定的代价:在堆里分配存储空间时会花掉更长的时间

4、 静态域(静态区):存放在对象中用static定义的静态成员。“静态”是指“位于固定位置”。程序运行期间,静态存储的数据将随时等候调用。可用static关键字指出一个对象的特定元素是静态的。但java对象本身永远都不会置入静态存储空间。

5、 常量池:存放常量。常数值通常直接置于程序代码内部。这样做是安全的。因为它们永远都不会改变,有的常数需要严格地保护,所以可考虑将它们置入只读存储器(ROM)。

6、 非RAM存储:硬盘等永久存储空间。若数据完全独立于一个程序之外,则程序不运行时仍可存在,并在程序的控制范围之外。其中两个最主要的例子便是“流式对象”和“固定对象”。对于流式对象,对象会变成字节流,通常会发给另一台机器,而对于固定对象,对象保存在磁盘中。即使程序中止运行,它们仍可保持自己的状态不变。对于这些类型的数据存储,它们能存在于其他媒体中,一旦需要,甚至能将它们恢复成普通的、基于RAM的对象。

在函数中定义的一些基本类型的变量数据和对象的引用变量都在函数的栈内存中分配。

当定义一个变量时,Java就在栈中为这个变量分配内存空间,当该变量退出该作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。 栈中的数据大小和生命周期是可以确定的,当没有引用指向数据时,这个数据就会消失。

1.每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(指向该对象的指针),对象的数据都存放在堆区中。 2.每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。 3.栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。

堆内存用来存放由new创建的对象和数组。在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理。

在堆中产生了一个数组或对象后,会在栈中定义一个特殊的引用变量,这个引用变量就是数组或对象在堆内存中的首地址,就相当于是为数组或对象在栈中起的一个名称,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或对象。引用变量就相当于是为数组或者对象起的一个名称。

堆中的数组和对象在没有栈中的引用变量指向它的时候,才变为垃圾,不能在被使用,但仍 然占据内存空间不放,在随后的一个不确定的时间被垃圾回收器收走(释放掉)。这也是 Java 比较占内存的原因。

1.存储的全部是对象,每个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令) 2.jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身

实际上,栈中的引用变量指向堆内存中的数据,这就是Java中的指针!

栈与堆比较

栈中主要存放一些基本类型的变量数据(int, short, long, byte, float, double, boolean, char)和对象引用(指向对象在堆内存的位置指针)。

堆是一个程序运行时,才会产生的数据存储区域。对象通过new,newarray等指令建立堆。它们不需要程序代码来显式的释放。堆是由垃圾回收来负责的。

栈的优势是,存取速度比堆要快,其次栈数据可以共享。但缺点是,存在栈中的数据大小与生存期是确定的。

堆的优势是可以动态地分配内存大小,因为它是在运行时动态分配内存的,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。

栈数据共享

栈有一个很重要的特殊性,就是存在栈中的数据可以共享

java
int a = 3;
int b = 3;

编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找栈中是否有3这个值,如果没找到,就将3存放进来,然后将a指向3。 接着处理int b = 3;在创建完b的引用变量后,因为在栈中已经有3这个值,便将b直接指向3。这样,就出现了a与b同时均指向3的情况。 它有利于节省空间。而一个对象引用变量修改了这个对象的内部状态,会影响到另一个对象引用变量。

静态域(静态区/方法区)

静态域

1.跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。 2.其中包含的都是在整个程序中永远唯一的元素,如class,static变量。

常量池 (constant pool)

常量池中包含各种基本类型(如int、long等等)和对象型(如String及数组)的常量值(final)。 还包含一些以文本形式出现的符号引用,比如:类和接口的全限定名;字段的名称和描述符;方法和名称和描述符。

例如:

java
String str = new String("abc");
String str = "abc";

第一种是用new()来新建对象的,它会在存放于堆中。每调用一次就会创建一个新的对象。 第二种是先在栈中创建一个对 String类的对象引用变量str,然后通过符号引用去字符串常量池 里找有没有"abc",如果没有,则将"abc"存放进字符串常量池 ,并令str指向”abc”,如果已经有”abc” 则直接令str指向“abc”。

String特殊的包装类

字符串是一个特殊包装类,其引用是存放在栈里的 而对象内容必须根据创建方式不同来决定是(常量池和堆).有的是编译期就已经创建好,存放在字符串常量池中,而有的是运行时才被创建.使用new关键字,存放在堆中。

java
String str = new String("abc");
String str = "abc";

上面字符串对象内容存在堆中,下面的存储在字符串常量池中

Released under the MIT License.