Android必须知道的Java内存结构及堆栈区别

一、认识Android储存结构

对于Android来说,存储主要分为三个部分:内存、内部存储以及外部存储,详细介绍如下:

(1)内存存储RAM(Random Access Memory) 内存与PC的内存是一样的,是用来运行程序,不能用来永久存储数据,手机一旦关机,在内存中的所有数据都将会丢失,内存也是现在人类制造的所有电子设备所必需拥有的。

(2)内部存储ROM(Read Only Memory) 就是就相当于是PC中的硬盘的角色。用于存储Andoid 设备的操作系统和应用程序的存储介质。也就是说,Android设备中的Android系统和应用程序(APK文件)都是存在内部存储区的。例如手机的/system/目录、/data/目录等。

(3)外部存储区 相当于PC中的U盘或者移动硬盘。

由于Android设备通常会将内部存储器芯片固定在芯片上,所以一般无法更换内部存储器的。 为了增强Android设备的存储能力,很多Android设备都支持扩展的SD卡功能(通常称之为MicroSD类型的存储卡)。所以我们经常说的3GB+32GB或者 3GB+64GB是指 内存是3GB大小、内部存储大小为32GB或者64GB大小。

二、 Java内存结构

1、栈区(stack),由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2、堆区(heap),一般由程序员分配释放, 若程序员不释放,JVM不定时观察发现没有引用指向时会GC回收 。
3、静态区(static storage),存放由static修饰的静态成员。 程序结束后由系统释放。
4、常量区(constant storage),基础类型、字符串等就是放在这里的。常量存储位于堆中。程序结束后由系统释放 。
5、代码区,存放函数体的二进制代码,而且是多个对象共享代码空间区域。

基本数据类型存在栈(stack), 引用数据类型存在堆(heap)。java数据类型结构图:

                                             ┏数值型━┳━整数型:byte short int long

              ┏基本数据类型━━┫                ┗━浮点型:float double

              ┃                            ┣字符型:char                                          

数据类型  ╋                            ┗布尔型:boolean              

              ┃                            ┏类(class)

              ┗引用数据类型━━╋接口(interface)

                                             ┗数组(array)

基本数据类型和引用数据类型的区别:

(1)从概念方面来说:

    基本数据类型:变量名指向具体的数值;

    引用数据类型:变量名指向存数据对象的内存地址,即变量名指向hash值。

(2)内存构建方面来说:

    基本数据类型:变量在声明之后java就会立刻分配给他内存空间;

    引用数据类型:这类变量声明时不会分配内存,只是存储了一个内存地址。

(3)从使用方面来说:

    基本数据类型:使用时需要赋具体值,判断时使用“==”号;

    引用数据类型:使用时可以赋null,判断时使用equals方法。

(4)从局部声明来说:(即局部变量)

    基本数据类型:所声明的变量名及值都是放在方法栈中;

    引用数据类型:所声明的变量(内存地址值)是放在方法的栈中,该变量所指向的对象实际是放在堆内存中。

(5)从全局声明来说:(即全局变量)

    基本数据类型:所声明的变量名及其值放在堆内存中;

    引用数据类型:所声明的变量(内存地址值)指向所引用的对象。该变量名和对象都存储在相应的堆内存中。

三、 堆和栈的区别

    栈与堆都是Java用来在Ram中存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。

    
Java的堆是一个运行时数据区,类的对象从中分配空间。这些对象通过new、new array、anewarray和 multianewarray等指令建立,它们不需要程序代码来显式的释放。堆是由垃圾回收来负责的,堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。

   
 Java的栈存取速度比堆要快,仅次于寄存器,栈数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。栈中主要存放一些基本类型的变量(int, short, long, byte, float, double, boolean, char)和对象句柄。栈有一个很重要的特殊性,就是存在栈中的数据可以共享。例如:

int a = 3;
int b = 3;

    编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找栈中是否有3这个值,如果没找到,就将3存放进来,然后将a指向3。接着处理int b = 3;在创建完b的引用变量后,因为在栈中已经有3这个值,便将b直接指向3。这样,就出现了a与b同时均指向3的情况,就可以使用 == 进行比较。另外,a的值改变并不影响到b的值,如果令a = 4;那么编译器会重新搜索栈中是否有4值,如果没有,则将4存放进来,并令a指向4;如果已经有了,则直接将 a 指向这个地址即可。

(为了更好理解各大区分配关系,专门配上了图示和小案例)

《Android必须知道的Java内存结构及堆栈区别》

听说理解上,小案例跟图示更配哦:

public class Test {
	String a = "123456";    //值在常量池中
        String b = "123456";   // a == b true 引用地址相同。因为值在常量池中已存在,所以两个变量指向的是同一个对象。
	final int A = 0; 		//final常量,堆中
	String[] c=new String[5]; 		// 手动分配new创建对象,在堆中
        static int d =0; 		//静态区
	public static void main(String args[]) () {//方法栈
	        a = new String("123"); //创建了两个String对象,一个存于常量池中,一个于堆内存中且由a指向。

                b = new String("123");//创建一个对象在堆内存中由b指向。因为常量池中已经存在"123"对象。

		String c = "123"; 	//值“123”在常量池堆中已存在。变量
	}
}

例如:String a = “123456”;  // 引用对象 a  ,值 123456 在常量池中。为什么呢?

    因为,基本数据类型存在栈(stack), String的对象实例存在堆(heap)。基础数据类型在栈里面直接分配内存 ,而引用数据则是通过堆里的对象来对栈中的内容进行引用。在 java 中 String 是个对象,是引用类型不是基本数据类型,判断是否相等,不能使用==,而使用equals方法。java中对 String 对象特殊对待,在堆区域给分成了两块,一块是 String constant pool(存储java字符串常量对象),另一块用于存储普通对象及字符串对象。另外,java虚拟机处理基础类型与引用类型的方式是不一样的,对于基本类型,java虚拟机会为其分配数据类型实际占用的内存空间;而对于引用类型变量,它仅仅是一个指向堆区中某个实例的指针。

字符串在内存中的存储情况如下所示:

String s1 = "abc";    
String s2 = "xyz";    
String s3 = "123";    
String s4 = "A";    
String s5 = newString("abc");     
char[] c = {'J','A','V','A'};     
String s6 = newString(c);      
String s7 = newString(newStringBuffer());  

《Android必须知道的Java内存结构及堆栈区别》

推荐阅读:

Java Final修饰符存储位置,为什么String是不可变的?

本人研究不深,若有错误请指正。

———————————————————————————结尾———————————————————————————

    原文作者:艾阳Blog
    原文地址: https://blog.csdn.net/csdn_aiyang/article/details/69380377
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞