java 内存 pdf_深入Java核心_Java内存分配原理精讲(完整版) PDF 下载

主要内容:

引言:栈、堆、常量池虽同属 Java 内存分配时操作的区域,但其适用范围和功用却大不相同。本文将深入

Java 核心,详细讲解 Java 内存分配方面的知识。

Java 内存分配与管理是 Java 的核心技术之一,之前我们曾介绍过 Java 的内存管理与

内存泄露以及 Java 垃圾回收方面的知识,今天我们再次深入 Java 核心,详细介绍一下 Java

在内存分配方面的知识。一般 Java 在内存分配时会涉及到以下区域:

◆寄存器:我们在程序中无法控制

◆栈:存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中

◆堆:存放用 new 产生的数据

◆静态域:存放在对象中用 static 定义的静态成员

◆常量池:存放常量

◆非 RAM 存储:硬盘等永久存储空间

Java 内存分配中的栈

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

当在一段代码块定义一个变量时,Java 就在栈中 为这个变量分配内存空间,当该变量退出

该作用域后,Java 会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作

他用。

Java 内存分配中的堆

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

回收器来管理。

在堆中产生了一个数组或对象后,还可以 在栈中定义一个特殊的变量,让栈中这个变量的

取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量。

引用变量就相当于是 为数组或对象起的一个名称,以后就可以在程序中使用栈中的引用变

量来访问堆中的数组或对象。引用变量就相当于是为数组或者对象起的一个名称。

引用变量是普通的变量,定义时在栈中分配,引用变量在程序运行到其作用域之外后被释放。

而数组和对象本身在堆中分配,即使程序 运行到使用 new 产生数组或者对象的语句所在的

代码块之外,数组和对象本身占据的内存不会被释放,数组和对象在没有引用变量指向它的

时候,才变为垃圾,不能在被使用,但仍 然占据内存空间不放,在随后的一个不确定的时

间被垃圾回收器收走(释放掉)。这也是 Java 比较占内存的原因。

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

常量池 (constant pool)

常量池指的是在编译期被确定,并被保存在已编译的.class 文件中的一些数据。除了包含

代码中所定义的各种基本类型(如 int、long 等等)和对象型(如 String 及数组)的常量

值(final)还包含一些以文本形式出现的符号引用,比如:

◆类和接口的全限定名;

◆字段的名称和描述符;

◆方法和名称和描述符。

虚拟机必须为每个被装载的类型维护一个常量池。常量池就是该类型所用到常量的一个有序

集和,包括直接常量(string,integer 和 floating point 常量)和对其他类型,字段和方

法的符号引用。

对于 String 常量,它的值是在常量池中的。而 JVM 中的常量池在内存当中是以表的形式存

在的, 对于 String 类型,有一张固定长度的 CONSTANT_String_info 表用来存储文字字符

串值,注意:该表只存储文字字符串值,不存储符号引 用。说到这里,对常量池中的字符

串值的存储位置应该有一个比较明了的理解了。

在程序执行的时候,常量池 会储存在 Method Area,而不是堆中。

堆与栈

Java 的堆是一个运行时数据区,类的(对象从中分配空间。这些对象通过 new、newarray、

anewarray 和 multianewarray 等指令建立,它们不需要程序代码来显式的释放。堆是由垃

圾回收来负责的,堆的优势是可以动态地分配内存 大小,生存期也不必事先告诉编译器,

因为它是在运行时动态分配内存的,Java 的垃圾收集器会自动收走这些不再使用的数据。

但缺点是,由于要在运行时动态 分配内存,存取速度较慢。

栈的优势是,存取速度比堆要快,仅次于寄存器,栈数据可以共享。但缺点是,存在栈中的

数据大小与生存期必须是 确定的,缺乏灵活性。栈中主要存放一些基本类型的变量数据(int,

short, long, byte, float, double, boolean, char)和对象句柄(引用)。

栈有一个很重要的特殊性,就是存在栈中的数据可以共享。假设我们同时定义:

1. Int a = 3;

2. Int b = 3;

编译器先处理 int a = 3;首先它会在栈中创建一个变量为 a 的引用,然后查找栈中是否有

3 这个值,如果没找到,就将 3 存放进来,然后将 a 指向 3。接着处理 int b = 3;在创建

完 b 的引用变量后,因为在栈中已经有 3 这个值,便将 b 直接指向 3。这样,就出现了 a 与 b 同时均指向 3 的情况。

这时,如果再令 a=4;那么编译器会重新搜索栈中是否有 4 值,如果没有,则将 4 存放进来,

并令 a 指向 4;如果已经有了,则直接将 a 指向这个地址。因此 a 值的改变不会影响 到 b

的值。

要注意这种数据的共享与两个对象的引用同时指向一个对象的这种共享是不同的,因为这种

情况 a 的修改并不会影响到 b, 它是由编译器完成的,它有利于节省空间。而一个对象引用

变量修改了这个对象的内部状态,会影响到另一个对象引用变量。

String 是一个特殊的包装类数据。可以用:

1. String str = new String("abc");

2. String str = "abc";

两种的形式来创建,第一种是用 new()来新建对象的,它会在存放于堆中。每调用一次就会

创建一个新的对象。而第二种是先在栈中创建一个对 String 类的对象引用变量 str,然后

通过符号引用去字符串常量池 里找有没有"abc",如果没有,则将"abc"存放进字符串常量

池 ,并令 str 指向”abc”,如果已经有”abc” 则直接令 str 指向“abc”。

比较类里面的数值是否相等时,用 equals()方法;当测试两个包装类的引用是否指向同一

个对象时,用==,下面用例子说明上面的理论。

1. String str1 = "abc";

2. String str2 = "abc";

3. System.out.println(str1==str2); //true

可以看出 str1 和 str2 是指向同一个对象的。

1. String str1 =new String ("abc");

2. String str2 =new String ("abc");

3. System.out.println(str1==str2); // false

用 new 的方式是生成不同的对象。每一次生成一个

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值